队列-ArrayDeque

81 阅读2分钟

image.png

一世相守铅华尽,人生至交莫如深。 (๑•̀ㅂ•́)و✧

ArrayDeque

基于数组

循环队列

实现了Deque这个接口来获得队列的功能

不允许插入null

从源码看,空元素对于 ArrayDeque 没有实际影响,可能是基于Deque的规范化建议

数组长度永远是2的指数幂

依于 构造函数扩容 的机制

便于循环队列的实现,详见 循环队列

类图

graph BT
Iterable --> Collection
Queue --> Iterable
AbstractCollection -.-> Iterable
Serializable
Cloneable
Deque --> Queue
ArrayDeque --> AbstractCollection
ArrayDeque -.-> Serializable
ArrayDeque -.-> Cloneable
ArrayDeque -.-> Deque

内部变量

// 数组容器
transient Object[] elements;

// 头元素 角标
transient int head;

// 尾元素 角标
transient int tail;

// 数组最小长度
private static final int MIN_INITIAL_CAPACITY = 8;

构造函数

无参构造

public ArrayDeque() {
    elements = new Object[16];
}

指定长度

public ArrayDeque(int numElements) {
    allocateElements(numElements); // elements = new Object[calculateSize(numElements)];
}


private static int calculateSize(int numElements) {
    int initialCapacity = MIN_INITIAL_CAPACITY;  // 8
    
    if (numElements >= initialCapacity) {
        // 计算大于numElements 的最小2指数幂
        // e.g. numElements = 10, 那 initialCapacity = 16
        // e.g. numElements = 17, 那 initialCapacity = 32
        
        initialCapacity = numElements;
        // e.p. 0..01...
        initialCapacity |= (initialCapacity >>>  1);
        // e.p. 0..011...
        initialCapacity |= (initialCapacity >>>  2);
        // e.p. 0..01111...
        initialCapacity |= (initialCapacity >>>  4);
        // e.p. 0..011111111...
        initialCapacity |= (initialCapacity >>>  8);
        // e.p. 0..01111111111111111...
        initialCapacity |= (initialCapacity >>> 16);
        
        initialCapacity++;
        // e.p. 0..1000000000000000...
        
        if (initialCapacity < 0)   // 越界处理
            initialCapacity >>>= 1;
            
    }
    return initialCapacity;
}

扩容

何时扩容

if (head == tail)
    doubleCapacity();

扩容操作

private void doubleCapacity() {
    assert head == tail;
    int p = head;
    int n = elements.length;
    int r = n - p;
    int newCapacity = n << 1;  // 数组长度 * 2
    if (newCapacity < 0) // 越界处理
        throw new IllegalStateException("Sorry, deque too big");
    Object[] a = new Object[newCapacity];
    System.arraycopy(elements, p, a, 0, r);
    System.arraycopy(elements, 0, a, r, p);
    elements = a;
    head = 0;
    tail = n;
}

看的累,直接举栗

image.png

循环队列

先明白一个位运算技巧

a & (elements.length - 1)

elements.length 就是数组长度,为 2的指数幂,二进制表示为 00100000...000

elements.length - 1 二进制表示为 00011111...111


elements.length > a >= 0 时

a & (elements.length - 1) == a

a = -1 时,-1 的二进制: 11111...11111

a & (elements.length - 1) == elements.length - 1

a = elements.length

a & (elements.length - 1) == 0

栈操作,移动 head

push

head = (head - 1) & (elements.length - 1)

这不就是向左移动吗

-1 时,就回到数组尾部 elements.length - 1

从这里能闻到循环的味道了


poll

head = (h + 1) & (elements.length - 1)

就是向右移动

elements.length时,就回到数组头部 0

队列操作,移动 tail

head 相反

addLast时,向右移动

tail = (tail + 1) & (elements.length - 1)

removeLast时,向左移动

tail = (tail - 1) & (elements.length - 1)