环形快递传送带大冒险: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 的魅力!

相关推荐
阿巴斯甜11 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker12 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952713 小时前
Andorid Google 登录接入文档
android
黄林晴14 小时前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿1 天前
Android MediaPlayer 笔记
android
Jony_1 天前
Android 启动优化方案
android
阿巴斯甜1 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇1 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_1 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android