环形快递传送带大冒险:ArrayDeque 的奇幻之旅

一、神奇的环形快递传送带

在 Java 王国的快递中心,有一台神奇的环形快递传送带(ArrayDeque),它和普通传送带不一样:普通传送带只能从一端放快递、另一端取快递,而环形传送带可以从两端任意放取快递,就像魔法一样!

这台传送带的秘密在于它的环形设计:当快递放到传送带末尾时,下一个快递可以接着放到开头,形成一个循环。快递员们可以从传送带的两头快速存取快递,效率极高!

java

arduino 复制代码
// 创建一个默认的环形传送带(初始容量16)
ArrayDeque<String> expressLine = new ArrayDeque<>();

二、传送带的内部构造

2.1 传送带的核心部件

环形传送带的关键部件包括:

  • 环形轨道(elements):存放快递的传送带,长度总是 2 的幂次方(8,16,32...),这样方便用魔法计算位置

  • 取件口(head):传送带取快递的位置

  • 放件口(tail):传送带放快递的下一个位置

  • 最小轨道长度(MIN_INITIAL_CAPACITY):至少能放 8 个快递

java

java 复制代码
transient Object[] elements; // 环形轨道
transient int head; // 取件口索引
transient int tail; // 放件口索引
private static final int MIN_INITIAL_CAPACITY = 8; // 最小轨道长度

2.2 传送带的建造方式

传送带可以按不同需求建造:

  • 标准传送带:new ArrayDeque()(默认 16 个位置)

  • 定制大小传送带:new ArrayDeque(20)(能放 20 个快递,实际会调整为 32,因为必须是 2 的幂)

  • 复制传送带:new ArrayDeque(existingExpress)(复制现有快递到新传送带)

java

ini 复制代码
// 建造一个能放100个快递的传送带(实际容量128,2的7次方是128)
ArrayDeque<Integer> customDeque = new ArrayDeque<>(100);

2.3 环形轨道的魔法循环

传送带的环形轨道用魔法公式计算位置:

  • 放快递到下一个位置:tail = (tail + 1) & (length - 1)

  • 取快递后移动取件口:head = (head + 1) & (length - 1)

比如长度为 8 的传送带,当 tail 到 7 时,下一个位置是 0,形成循环:

java

ini 复制代码
// 模拟环形轨道操作
Object[] track = new Object[8];
int head = 0, tail = 0;

// 放第一个快递
track[tail] = "快递1";
tail = (tail + 1) & (track.length - 1); // tail=1

// 放第二个快递
track[tail] = "快递2";
tail = (tail + 1) & (track.length - 1); // tail=2

// 取快递
Object express = track[head]; // 快递1
head = (head + 1) & (track.length - 1); // head=1

三、传送带的工作流程

3.1 快递员放快递(插入操作)

3.1.1 紧急快递优先放(addFirst)

当有紧急快递时,快递员会把它放到取件口前面,这样下一个就会被取走:

java

arduino 复制代码
// 放紧急快递到取件口前
expressLine.addFirst("加急文件");

3.1.2 普通快递按顺序放(addLast)

普通快递放到传送带末尾:

java

arduino 复制代码
// 放普通快递到末尾
expressLine.addLast("普通包裹");
expressLine.addLast("另一个普通包裹");

3.1.3 传送带扩容(doubleCapacity)

当传送带满了(head==tail),会启动扩容魔法:

  1. 建造一个两倍长的新传送带

  2. 把旧传送带上的快递搬到新传送带上

  3. 调整取件口和放件口位置

java

ini 复制代码
// 扩容魔法简化示意
private void doubleCapacity() {
    // 旧传送带长度
    int oldLength = elements.length;
    // 新传送带长度是旧的2倍
    int newLength = oldLength << 1;
    Object[] newTrack = new Object[newLength];
    
    // 搬运快递:先搬取件口到末尾的,再搬开头到取件口前的
    System.arraycopy(elements, head, newTrack, 0, oldLength - head);
    System.arraycopy(elements, 0, newTrack, oldLength - head, head);
    
    elements = newTrack;
    head = 0;
    tail = oldLength;
}

3.2 快递员取快递(删除操作)

3.2.1 取最前面的快递(removeFirst)

从取件口取走第一个快递:

java

ini 复制代码
// 取最前面的快递
String firstExpress = expressLine.removeFirst();

3.2.2 取最后面的快递(removeLast)

从传送带末尾取快递:

java

ini 复制代码
// 取最后面的快递
String lastExpress = expressLine.removeLast();

3.3 查看快递(查看操作)

3.3.1 看最前面的快递(getFirst)

不取出,只查看取件口的快递:

java

ini 复制代码
// 查看最前面的快递
String firstExpress = expressLine.getFirst();

3.3.2 看最后面的快递(getLast)

查看传送带末尾的快递:

java

ini 复制代码
// 查看最后面的快递
String lastExpress = expressLine.getLast();

四、传送带的特殊功能

4.1 传送带遍历员(迭代器)

有一个专门的遍历员可以沿着传送带走一圈,按顺序查看所有快递:

java

scss 复制代码
// 遍历传送带上的所有快递
for (String express : expressLine) {
    System.out.println(express);
}

4.2 多快递员问题(线程安全)

如果多个快递员同时操作传送带,可能会混乱。这时候需要用安全传送带(LinkedBlockingDeque):

java

arduino 复制代码
// 安全传送带,多线程可用
LinkedBlockingDeque<String> safeLine = new LinkedBlockingDeque<>();

五、环形传送带的应用场景

5.1 叠盘子游戏(栈实现)

传送带可以当栈用,就像叠盘子,先叠的在下面,后叠的在上面:

java

arduino 复制代码
// 用传送带当栈
ArrayDeque<Integer> stack = new ArrayDeque<>();
stack.addFirst(1); // 压入1
stack.addFirst(2); // 压入2,现在栈顶是2
int top = stack.removeFirst(); // 弹出2,栈里剩下1

5.2 排队买咖啡(队列实现)

也可以当普通队列,先到先得:

java

arduino 复制代码
// 用传送带当队列
ArrayDeque<String> queue = new ArrayDeque<>();
queue.addLast("顾客A"); // 排队
queue.addLast("顾客B");
String first = queue.removeFirst(); // 顾客A先买到咖啡

5.3 滑动窗口找最大值(滑动窗口算法)

比如计算最近 3 天的最高温度,传送带只保留窗口内的温度,并维护最大值:

java

arduino 复制代码
// 滑动窗口找最大值
public int[] maxSlidingWindow(int[] nums, int k) {
    ArrayDeque<Integer> deque = new ArrayDeque<>();
    int[] result = new int[nums.length - k + 1];
    int index = 0;
    
    for (int i = 0; i < nums.length; i++) {
        // 移除窗口外的索引
        while (!deque.isEmpty() && deque.peekFirst() <= i - k) {
            deque.pollFirst();
        }
        // 移除比当前值小的索引,保持队列降序
        while (!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]) {
            deque.pollLast();
        }
        deque.offerLast(i);
        // 窗口形成时记录最大值
        if (i >= k - 1) {
            result[index++] = nums[deque.peekFirst()];
        }
    }
    return result;
}

5.4 迷宫探险(BFS 算法)

传送带可以用来按层探索迷宫,就像逐层搜索:

java

arduino 复制代码
// BFS迷宫探险
class Maze {
    void bfs(int start) {
        ArrayDeque<Integer> queue = new ArrayDeque<>();
        boolean[] visited = new boolean[100]; // 假设迷宫100个节点
        
        queue.offerLast(start);
        visited[start] = true;
        
        while (!queue.isEmpty()) {
            int current = queue.pollFirst();
            System.out.println("探索节点: " + current);
            
            // 探索所有相邻节点
            for (int neighbor : getNeighbors(current)) {
                if (!visited[neighbor]) {
                    visited[neighbor] = true;
                    queue.offerLast(neighbor);
                }
            }
        }
    }
}

六、快递传送带常见问题

6.1 传送带太小怎么办?

如果知道有很多快递,提前设置大一点的传送带,避免频繁扩容:

java

arduino 复制代码
// 预估放100个快递,设置初始容量128
ArrayDeque<String> bigLine = new ArrayDeque<>(128);

6.2 想放 null 快递?

传送带不接受 null 快递,放的时候会报错,需要提前检查:

java

ini 复制代码
String express = getExpress();
if (express != null) {
    expressLine.addLast(express);
}

6.3 传送带太慢?

传送带的操作大部分是 O (1) 的,比如头尾插入删除,比 LinkedList 随机访问快,但不支持快速随机访问,适合头尾操作场景。

七、传送带的未来升级

Java 王国的工匠们还在优化传送带:

  • 更快的扩容算法,减少快递搬运时间

  • 多线程安全的传送带升级版本

  • 支持更多魔法操作,比如批量处理快递

通过这台神奇的环形快递传送带(ArrayDeque),我们可以高效地处理各种需要双端操作的数据,就像快递员高效处理快递一样,这就是 ArrayDeque 的魅力!

相关推荐
赏金术士33 分钟前
第六章:UI组件与Material3主题
android·ui·kotlin·compose
TechMerger2 小时前
Android 17 重磅重构!服役 20 年的 MessageQueue 迎来无锁改造,卡顿大幅优化!
android·性能优化
yuhuofei20214 小时前
【Python入门】Python中字符串相关拓展
android·java·python
dalancon5 小时前
Android Input Spy Window
android
dalancon6 小时前
InputDispatcher派发事件,查找目标窗口
android
我命由我123456 小时前
Android Framework P3 - MediaServer 进程、认识 ServiceManager 进程
android·c语言·开发语言·c++·visualstudio·visual studio·android runtime
天才少年曾牛7 小时前
Android14 新增系统服务后,应用调用出现 “hidden api” 警告的原因与解决方案
android·frameworks
赏金术士7 小时前
Jetpack Compose 底部导航实战教程(完整版)
android·kotlin·compose
随遇丿而安8 小时前
第5周:XML 资源、样式和主题,真正解决的是“页面以后还改不改得动”
android
zh_xuan8 小时前
Android 获取系统内存页大小:sysconf(_SC_PAGESIZE) 与 JNI 实现
android·jni·ndk·内存页大小