【Java原码阅读】——ArrayDeque的底层扩容机制

ArrayDeque是一个双端队列,基于循环数组实现,初始容量为16。当添加元素导致队列满时,会进行扩容,扩容策略为翻倍。若自定义容量小于8,则使用8作为容量;大于8时,会选择最接近且大于自定义值的2的幂次作为容量。
摘要由CSDN通过智能技术生成

ArrayDeque是Deque接口的子实现,是一个双端队列。
有序(读取顺序和写入顺序完全一致或完全相反)
可以存储null
可以存储重复元素
ArrayDeque的底层是一个循环队列,大大节约了空间。
底层数组的默认大小是16,当然也可以在创建对象的时候自定义容量。让我们来看一下这个队列底层是怎么实现创建数组和扩容的。

// 底层的循环队列
// transient表示不能被序列化
transient Object[] elements;

// 创建容量为16的Object数组  
public ArrayDeque() {
    elements = new Object[16];
}  

// 调用offer方法插入新元素
// 在队列末尾插入元素
public boolean offer(E e) {
    return offerLast(e);
}
public boolean offerLast(E e) {
    addLast(e);
    return true;
}

public void addLast(E e) {
    // 不能在队列中插入null
    if (e == null)
        throw new NullPointerException();
    // tail指向队列中下一个待插入的位置
    // 队尾入队
    elements[tail] = e;

    // 底层循环队列采取的是牺牲一个存储空间来判断队列是否已满
    // (tail + 1) & (elements.length - 1))相当于(tail+1)%len
    // 判断当前队列是否满了
    if ( (tail = (tail + 1) & (elements.length - 1)) == head)
        // 扩容2倍
        doubleCapacity();
}

private void doubleCapacity() {
    assert head == tail;
    int p = head;
    int n = elements.length;
    int r = n - p; // number of elements to the right of p

    // newCapacity=n*2
    int newCapacity = n << 1;

    // newCapacity超出int的最大正整数值
    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;
}

默认容量16是2的幂次,所以每次扩容只要右移一位,就可以实现扩容一倍。
但是如果是自定义的非2的整数次幂的初始容量,底层是如何实现扩容的呢?

// 传入自定义的容量,创建对象
public ArrayDeque(int numElements) {
    // 调用方法,初始化底层循环队列
    allocateElements(numElements);
}

private void allocateElements(int numElements) {
    elements = new Object[calculateSize(numElements)];
}

private static int calculateSize(int numElements) {
    //                      8
    int initialCapacity = MIN_INITIAL_CAPACITY;


    // 自定义的容量不超过8,则数组容量定位8
    // 超过8,则重新生成一个离initialCapacity最近的2的整数次幂的值
    if (numElements >= initialCapacity) {
        initialCapacity = numElements;

        // 假设initialCapacity最高位的1在第HB个位置上

        // >>> 表示逻辑移位
        // 先逻辑右移一位,再和原数进行或操作
        // (后面的数我们先不管,只看前面的1)
        // 右移使得最高位的1右移一位,‘|’ 操作使得这个1前面又出现了一个1
        initialCapacity |= (initialCapacity >>>  1);

        // 右移两位,再或运算,使得前面出现4个1
        initialCapacity |= (initialCapacity >>>  2);
        initialCapacity |= (initialCapacity >>>  4);
        initialCapacity |= (initialCapacity >>>  8);
        initialCapacity |= (initialCapacity >>> 16);
        // 多次移位使得这个数从HB开始都是1
        // +1,使得其变为2的HB次
        initialCapacity++;

        // 越界 
        if (initialCapacity < 0) 
            // 再逻辑右移一次,initialCapacity=2^30
            initialCapacity >>>= 1;
    }
    return initialCapacity;
}

综上,当我们自定义ArrayDeque底层队列的容量时,如果传入的容量小于8,那么jvm就会创建一个容量为8的循环队列。如果传入的容量大于8,那么队列的容量就是离自定义值最接近(大于)的2的整数次幂。
ty;
}

综上,当我们自定义ArrayDeque底层队列的容量时,如果传入的容量小于8,那么jvm就会创建一个容量为8的循环队列。如果传入的容量大于8,那么队列的容量就是离自定义值最接近(大于)的2的整数次幂。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值