队列总结(三)ArrayDeque

ArrayDeque

关于Deque接口的介绍,记录在了另一文章里https://blog.csdn.net/java_lifeng/article/details/120650857

ArrayDeque 是 Deque 接口的一种具体实现,是依赖于可变数组来实现的。ArrayDeque 没有容量限制,可根据需求自动进行扩容。ArrayDeque不支持值为 null 的元素。

ArrayDeque 不是线程安全的; 在没有外部同步的情况下,它们不支持多线程并发访问。 此类用作Stack时可能比Stack快,用作队列时比LinkedList快。大多数ArrayDeque操作在分摊的常数时间内运行。 例外情况包括remove 、 removeFirstOccurrence 、 removeLastOccurrence 、 contains 、 iterator.remove()和批量操作,所有这些都在线性时间内运行。

源码分析

ArrayDeque是基于数组的实现,但在ArrayDeque中,数组是作为环形来使用的

这个代码在HashMap中也很熟悉,ArrayDeque默认数组长度为16,如果手动指定为numElements,则根据此参数得到大于等于此数值的2^n形式的值,即ArrayDeque的数组长度大小必须是2的次方数。

    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;
    }

从尾部插入的元素(addLast)从下标0开始正序插入,用tail字段表示下一个将要从尾部插入的元素的下标。(tail - 1) & (elements.length - 1)表示队列尾部实际的元素坐标。
从头部插入的元素(addFirst)从下标length-1开始倒序插入,用head表示当前队列中头部元素的下标,从头部插入时,根据head计算要插入的下标,即head = (head - 1) & (elements.length - 1)

    // 队列中头元素的下标,注意,基于数组实现,但不是数组中的下标0
    transient int head;
    public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[head = (head - 1) & (elements.length - 1)] = e;
        // 说明数组空间用尽,下一次插入会产生覆盖,需要扩容
        if (head == tail)
            doubleCapacity();
    }
    
    public E getFirst() {
        @SuppressWarnings("unchecked")
        E result = (E) elements[head];
        if (result == null)
            throw new NoSuchElementException();
        return result;
    }

head初始值为0, (head - 1) & (elements.length - 1),-1与上任何数得原数,即第一次调用addFirst之后,head = elements.length - 1。此后,根据与运算的特性,每次调用addFirst,head-1,直至head == tail,触发数组扩容后head重新置为0

	 // 下一个插入队尾的元素的下标,注意,是下一个
    transient int tail;
    public void addLast(E e) {
        if (e == null)
            throw new NullPointerException();
        elements[tail] = e;
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }
    
    public E getLast() {
        @SuppressWarnings("unchecked")
        // 因为tail代表的是下一个,所以实际意义上的最后一个是tail-1
        E result = (E) elements[(tail - 1) & (elements.length - 1)];
        if (result == null)
            throw new NoSuchElementException();
        return result;
    }

扩容操作,懒得分析了,个人感觉整体设计十分精妙

    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;
    }

Deque接口不提供支持下标操作的api,比如List接口中的get(i),remove(i),ArrayDeque同样也不支持。对于删除,提供了删除第一次出现和删除最后一次出现的操作。

    public boolean remove(Object o) {
        return removeFirstOccurrence(o);
    }
    
    public boolean removeFirstOccurrence(Object o) {
        if (o == null)
            return false;
        int mask = elements.length - 1;
        int i = head;
        Object x;
        while ( (x = elements[i]) != null) {
            if (o.equals(x)) {
                delete(i);
                return true;
            }
            i = (i + 1) & mask;
        }
        return false;
    }
    
    public boolean removeLastOccurrence(Object o) {
        if (o == null)
            return false;
        int mask = elements.length - 1;
        int i = (tail - 1) & mask;
        Object x;
        while ( (x = elements[i]) != null) {
            if (o.equals(x)) {
                delete(i);
                return true;
            }
            i = (i - 1) & mask;
        }
        return false;
    }

上面的逻辑很简单,主要看delete方法,有点绕,烦死了,没劲看了


 /**
  * 移除元素数组中指定位置的元素,并根据需要调整头部和尾部。
  * 这可能导致数组中元素的移动。
  */
  private boolean delete(int i) {
        checkInvariants();
        final Object[] elements = this.elements;
        final int mask = elements.length - 1;
        final int h = head;
        final int t = tail;
        final int front = (i - h) & mask;
        final int back  = (t - i) & mask;

        // Invariant: head <= i < tail mod circularity
        if (front >= ((t - h) & mask))
            throw new ConcurrentModificationException();

        // Optimize for least element motion
        if (front < back) {
            if (h <= i) {
                System.arraycopy(elements, h, elements, h + 1, front);
            } else { // Wrap around
                System.arraycopy(elements, 0, elements, 1, i);
                elements[0] = elements[mask];
                System.arraycopy(elements, h, elements, h + 1, mask - h);
            }
            elements[h] = null;
            head = (h + 1) & mask;
            return false;
        } else {
            if (i < t) { // Copy the null tail as well
                System.arraycopy(elements, i + 1, elements, i, back);
                tail = t - 1;
            } else { // Wrap around
                System.arraycopy(elements, i + 1, elements, i, mask - i);
                elements[mask] = elements[0];
                System.arraycopy(elements, 1, elements, 0, t);
                tail = (t - 1) & mask;
            }
            return true;
        }
    }
    
    private void checkInvariants() {
        assert elements[tail] == null;
        assert head == tail ? elements[head] == null :
            (elements[head] != null &&
             elements[(tail - 1) & (elements.length - 1)] != null);
        assert elements[(head - 1) & (elements.length - 1)] == null;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值