ArrayDeque解析

ArrayDeque解析

ArrayDeque的特点

  • 大小自增长的队列
  • 内部使用数组存储数据
  • 线程不安全
  • 内部数组长度为8、16、32….. 2的n次方
  • 头指针head从内部数组的末尾开始,尾指针tail从0开始,在头部插入数据时,head减一,在尾部插入数据时,tail加一。当head==tail时说明数组的容量满足不了当前的情况,此时需要扩大容量为原来的二倍。

核心思想图

image

代码解析

  • 分配数组大小
private void allocateElements(int numElements) {
        int initialCapacity = MIN_INITIAL_CAPACITY;
        // 如果指定的数组长度大于最小的值,需要计算数组大小。
        // 算法利用或运算和右移运算,计算结果始终为2的n次方,比如numElements的值为4、5、6、7时,initialCapacity的结果为8;numElements的值为8、9、10、11、12、13、14、15时,initialCapacity的结果为16。
        // 设计数组的长度为2的n次方是为循环链表考虑的,后面会解释。
        if (numElements >= initialCapacity) {
            initialCapacity = numElements;
            initialCapacity |= (initialCapacity >>>  1);
            initialCapacity |= (initialCapacity >>>  2);
            initialCapacity |= (initialCapacity >>>  4);
            initialCapacity |= (initialCapacity >>>  8);
            initialCapacity |= (initialCapacity >>> 16);
            initialCapacity++;
            // 如果值太大溢出(即值小于0),需要缩小2倍
            if (initialCapacity < 0)   // Too many elements, must back off
                initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
        }
        elements = new Object[initialCapacity];
    }
  • 扩大数组长度
private void doubleCapacity() {
        // assert head == tail;
        int p = head;
        int n = elements.length;
         // 头指针右边的长度
        int r = n - p;
        // 扩大2倍
        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;
        // 初始化头指针为0
        head = 0;
        // 初始化尾指针为n
        tail = n;
    }
  • 在头部插入数据
public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException("e == null");
        // 在头部插入数据,头指针向左移动1,即减一,elements.length为2的n次方,所以elements.lenght-1的二进制表示为1111....1111。
        // 特殊情况:假设数组的长度为16,初始化时head为0,那么(head - 1) & (elements.length - 1)就是-1 & 15的值,负数的二进制为对应正数的二进制的补码,-1即为11111....1111,那么-1 & 15 = 15,如上图,当首次向队列头部插入数据时,head在数组的末尾。
        elements[head = (head - 1) & (elements.length - 1)] = e;
        // 头指针和尾指针相等时(即相遇),表示当前的数组长度不够,需要扩大数组长度
        if (head == tail)
            doubleCapacity();
    }
  • 在尾部插入数据
public void addLast(E e) {
        if (e == null)
            throw new NullPointerException("e == null");
        elements[tail] = e;
        // tail向右移动,如果头指针和尾指针相等时(即相遇),表示当前的数组长度不够,需要扩大数组长度
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }
  • 遍历
private class DeqIterator implements Iterator<E> {
        // 从头指针位置开始遍历
        private int cursor = head;
        private int fence = tail;
        private int lastRet = -1;

        public boolean hasNext() {
            return cursor != fence;
        }

        public E next() {
            if (cursor == fence)
                throw new NoSuchElementException();
            @SuppressWarnings("unchecked") E result = (E) elements[cursor];

            if (tail != fence || result == null)
                throw new ConcurrentModificationException();
            lastRet = cursor;
            // 每次加一,利用与运算,形成循环。比如,数组的长度为16,当cursor递增到15时,下面的计算应为16 & 15 = 0,cursor的值变为了0.
            cursor = (cursor + 1) & (elements.length - 1);
            return result;
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            if (delete(lastRet)) { // if left-shifted, undo increment in next()
                cursor = (cursor - 1) & (elements.length - 1);
                fence = tail;
            }
            lastRet = -1;
        }
    }
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值