ArrayDeque部分源码剖析

笔者所用JDK为 RedHatOpenJDK-8u282-win64 版。

  1. ArrayDeque,数组实现的双端队列。核心思想是维护头尾指针,将数组变为循环数组(头指针指向头元素,尾指针指向尾元素后的空位)。无法按索引取值(因为没有固定的索引)。不允许插入 Null。用作栈时,比Stack快;用作队列时,比LinkedList快。缺点是:线程不安全,空间浪费。

  2. 扩容机制:双倍扩容,数组长度永远是2的幂(最小8,好处是扩容、取模时只需位运算即可)。

  3. 构造器函数,无参时,创建长度为16的数组;有参时,调用allocateElements()

    public ArrayDeque() {
        elements = new Object[16];
    }
    public ArrayDeque(int numElements) {
        allocateElements(numElements);
    }
    public ArrayDeque(Collection<? extends E> c) {
        allocateElements(c.size());
        addAll(c);
    }
    
  4. allocateElements(),按输入int形参,调用calculateSize(),分配长度为2的幂次的数组。

    /**
     * Allocates empty array to hold the given number of elements.
     *
     * @param numElements  the number of elements to hold
     */
    private void allocateElements(int numElements) {
        elements = new Object[calculateSize(numElements)];
    }
    
  5. calculateSize(),若 numElements 小于8,分配8;否则,分配大于 numElements 的最小2幂次(若 numElements 已为2幂次,翻倍),最多为2^30。源码使用位运算实现,通过不断移位2的幂次再或,将 numElements 的最高位 1 向后传播

    private static final int MIN_INITIAL_CAPACITY = 8;
    private static int calculateSize(int numElements) {
        int initialCapacity = MIN_INITIAL_CAPACITY;
        // Find the best power of two to hold elements.
        // Tests "<=" because arrays aren't kept full.
        if (numElements >= initialCapacity) {
            initialCapacity = numElements;
            initialCapacity |= (initialCapacity >>>  1);
            initialCapacity |= (initialCapacity >>>  2);
            initialCapacity |= (initialCapacity >>>  4);
            initialCapacity |= (initialCapacity >>>  8);
            initialCapacity |= (initialCapacity >>> 16);
            initialCapacity++;
    
            if (initialCapacity < 0)   // Too many elements, must back off
                initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
        }
        return initialCapacity;
    }
    
  6. addFirst()位运算实现取模避免 head 越界(说穿了就是在其等于-1时转换为1),若插入后数组存满(head==tail),调用 doubleCapacity() 扩容数组长翻倍

    那要是插入前就满了呢?该情况不会发生。首先,构造函数保证数组必有空余;其次,每次插入后都会进行判断,及时扩容。

    /**
     * Inserts the specified element at the front of this deque.
     *
     * @param e the element to add
     * @throws NullPointerException if the specified element is null
     */
    public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[head = (head - 1) & (elements.length - 1)] = e;
        if (head == tail)
            doubleCapacity();
    }
    
  7. doubleCapacity(),调用 native 方法 System.arraycopy(),先复制 head 右半段,再复制左半段。

    /**
     * Doubles the capacity of this deque.  Call only when full, i.e.,
     * when head and tail have wrapped around to become equal.
     */
    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
        int newCapacity = n << 1;
        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;
    }
    
  8. pollFirst(),从头部移出一个元素,若为null,返回null。

    public E pollFirst() {
        int h = head;
        @SuppressWarnings("unchecked")
        E result = (E) elements[h];
        // Element is null if deque empty
        if (result == null)
            return null;
        elements[h] = null;     // Must null out slot
        head = (h + 1) & (elements.length - 1);
        return result;
    }
    
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值