java8 ArrayDeque接口实现源码解析

一、类继承关系

Deque接口说明和使用参考上一篇java8 LinkedList接口实现源码解析 

二、接口实现

      ArrayDeque是基于数组的Deque接口实现类,内存存储结构简单,当需要FIFO队列,LIFO队列或者Stack时,官方推荐优先使用ArrayDeque。线程不安全,会自动扩容,遍历时修改会快速失败。

1、全局变量和公共方法

/**
     * 保存元素的数组,数组的长度总是2的整数次方,数组不允许填满,同时保证没有保存元素的数组位都是null
     */
    transient Object[] elements; // non-private to simplify nested class access

    /**
     * 头元素的对应的数组下标,从最大值开始递减,未插入时head为0
     */
    transient int head;

    /**
     * 下一个尾元素对应的数组下标,从0开始递增,当等于head时表示数组已满
     */
    transient int tail;

    /**
     * 数组的最低容量
     */
    private static final int MIN_INITIAL_CAPACITY = 8;

     
    private static int calculateSize(int numElements) {
        int initialCapacity = MIN_INITIAL_CAPACITY;
        //计算大于numElements的最小的2的整数次方,同HashMap
        if (numElements >= initialCapacity) {
            initialCapacity = numElements;
            initialCapacity |= (initialCapacity >>>  1);
            initialCapacity |= (initialCapacity >>>  2);
            initialCapacity |= (initialCapacity >>>  4);
            initialCapacity |= (initialCapacity >>>  8);
            initialCapacity |= (initialCapacity >>> 16);
            initialCapacity++;

            //超过了int类型的最大值变成负数了
            if (initialCapacity < 0)   // Too many elements, must back off
                initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
        }
        return initialCapacity;
    }

    /**
     * 初始化数组
     */
    private void allocateElements(int numElements) {
        elements = new Object[calculateSize(numElements)];
    }

    //数组扩容
    private void doubleCapacity() {
        //head等于tail即表明数组已经填满了
        assert head == tail;
        int p = head;
        int n = elements.length;
        int r = n - p; // 即数组下标为head的右边的元素的个数,即通过addFirst添加的元素的个数
        //扩容一倍
        int newCapacity = n << 1;
        //超过int的最大值变成负值
        if (newCapacity < 0)
            throw new IllegalStateException("Sorry, deque too big");
        Object[] a = new Object[newCapacity];
        //将通过addFirst添加的元素和通过addLast添加的元素整体交换位置,head变成0,tail变成n,
        // 这样下一次插入元素就可以直接插入到扩容的那部分数组里面,同时保证了pollLast()和pollFirst()时元素的顺序仍然
        //是之前插入的顺序
        System.arraycopy(elements, p, a, 0, r);
        System.arraycopy(elements, 0, a, r, p);
        elements = a;
        head = 0;
        tail = n;
    }

   //将列表中元素复制到一个新数组中
    private <T> T[] copyElements(T[] a) {
        //假定数组a足够大,大于size()
        if (head < tail) {//只有当head为0即没有调用addFirst时head才会小于tail
            System.arraycopy(elements, head, a, 0, size());
        } else if (head > tail) {//数组的头部和尾部都有元素
            int headPortionLen = elements.length - head;//头部元素的个数
            //复制头部元素到新数组的前面
            System.arraycopy(elements, head, a, 0, headPortionLen);
            //复制尾部元素到新数组的后面,从而保证对新数组遍历时返回元素的顺序依然是元素添加的顺序
            System.arraycopy(elements, 0, a, headPortionLen, tail);
        }
        return a;
    }

2、构造方法


    public ArrayDeque() {
        elements = new Object[16];
    }//默认数组长度是16


    public ArrayDeque(int numElements) {
        allocateElements(numElements);
    }


    public ArrayDeque(Collection<? extends E> c) {
        allocateElements(c.size());
        addAll(c);
    }

3、元素插入

   public void addFirst(E e) {
        if (e == null)
            throw new NullPointerException();
        //head的初始值为0,每次addFirst都会减一,
        //以数组长度16为例,head为0时head变成15,下一次调用head变成14,一直到head等于tail执行扩容,head又变成0
        elements[head = (head - 1) & (elements.length - 1)] = e;
        if (head == tail)
            doubleCapacity();
    }

    //等价于add()
    public void addLast(E e) {
        if (e == null)
            throw new NullPointerException();
        //以数组长度16为例,tail的初始值为0,然后是1,2,一直到head等于tail
        elements[tail] = e;
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }

3、元素删除

public E pollFirst() {
        int h = head;
        @SuppressWarnings("unchecked")
        E result = (E) elements[h];//取出head对应的元素
        if (result == null)//队列为空,不需要重置head
            return null;
        elements[h] = null;
        //重置head,addFirst是减一,此处就加1
        head = (h + 1) & (elements.length - 1);
        return result;
    }

    public E pollLast() {
        int t = (tail - 1) & (elements.length - 1);//取出上一次添加的元素,addLast时加1了,此处就减一
        @SuppressWarnings("unchecked")
        E result = (E) elements[t];
        if (result == null) //队列为空,无需重置tail
            return null;
        elements[t] = null;
        tail = t;//重置tail
        return result;
    }

public boolean removeFirstOccurrence(Object o) {
        if (o == null)
            return false;
        int mask = elements.length - 1;
        int i = head;
        Object x;
        //按照元素添加addFirst的顺序遍历查找
        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;
        //按照元素添加addLast的顺序遍历查找
        while ( (x = elements[i]) != null) {
            if (o.equals(x)) {
                delete(i);
                return true;
            }
            i = (i - 1) & mask;
        }
        return false;
    }


//该方法删除元素会导致元素往数组的前后移动,数组下标为0的那一端为后,向前移动时tail不变,head减1,返回false,想后移动时tail减1,head不变,返回true
    //假定i位于数组的索引范围内
    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;
        //(i - h) & mask表示在双向链表中目标索引i前面的元素个数,以数组长度8为例,t为2,h为5,当i为5时,计算结果为0,当i为1时,计算结果为4
        final int front = (i - h) & mask;
        //(t - i) & mask表示在双向链表中目标索引i后面的元素个数,以数组长度8为例,t为2,h为5,当i为5时,计算结果为5,当i为1时,计算结果为1
        final int back  = (t - i) & mask;

        //(t - h) & mask表示数组中已经插入的元素的个数,以数组长度8为例,t为2,h为5,算出来的结果为5
        //综上此表达式成立表示tail<=i<head,此时i对应的数组位为null
        if (front >= ((t - h) & mask))
            throw new ConcurrentModificationException();

        //判断目标索引位于双向链表中的前半部分还是后半部分,从而决定移动的方向,保证需要移动的元素的个数最少
        if (front < back) {//位于双向链表中前半部分,将目标元素之前的元素往后移动即可,双向链表中往后在数组中是往前
            if (h <= i) {
                //被删除元素位于头部,将该元素之后插入的front个元素往前挪一位
                System.arraycopy(elements, h, elements, h + 1, front);
            } else {
                //被删除元素位于尾部,将目标索引i之前的元素往前挪一位,把第一次掉addFirst插入的元素放到下标为0的位置
                System.arraycopy(elements, 0, elements, 1, i);
                elements[0] = elements[mask];
                //将头部的元素整体往后移一位
                System.arraycopy(elements, h, elements, h + 1, mask - h);
            }
            //h对应的数组位置null,重置head
            elements[h] = null;
            head = (h + 1) & mask;
            return false;
        } else {
            if (i < t) {
                //被删除元素位于尾部,将目标索引i之后的back个元素往后移动一位
                System.arraycopy(elements, i + 1, elements, i, back);
                //重置tail
                tail = t - 1;
            } else {
                //被删除元素位于头部,将目标索引i之后的mask-i个元素往后移动一位,用数组下标为0的元素填充数组下标最大的元素
                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;
        }
    }

4、元素查找

 public boolean contains(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))
                return true;
            i = (i + 1) & mask;
        }
        return false;
    }

5、元素遍历

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;
            //同pollFirst()方法
            cursor = (cursor + 1) & (elements.length - 1);
            return result;
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            //delete方法返回true时,tail减一,head不变,这时元素往数组下标为0的方向移动了一位,
            //即往与遍历方向相反的方向移动了一位,为了保证下一个元素依然不变,需要将cursor减1,
            //tail减一了,fence需要重置
            //delete方法返回false时,tail不变,head减一,这时是之前遍历过的元素向着遍历方向移动了一位
            //所以cursor不变
            if (delete(lastRet)) {
                cursor = (cursor - 1) & (elements.length - 1);
                fence = tail;
            }
            lastRet = -1;
        }

        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            Object[] a = elements;
            int m = a.length - 1, f = fence, i = cursor;
            cursor = f;
            while (i != f) {
                @SuppressWarnings("unchecked") E e = (E)a[i];
                i = (i + 1) & m;
                if (e == null)
                    throw new ConcurrentModificationException();
                action.accept(e);
            }
        }
    }

    private class DescendingIterator implements Iterator<E> {
        /*
         * 倒序遍历从尾部开始
         */
        private int cursor = tail;
        //元素遍历的索引边界
        private int fence = head;
        private int lastRet = -1;

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

        public E next() {
            if (cursor == fence)
                throw new NoSuchElementException();
            //同pollLast()的逻辑
            cursor = (cursor - 1) & (elements.length - 1);
            @SuppressWarnings("unchecked")
            E result = (E) elements[cursor];
            if (head != fence || result == null)
                throw new ConcurrentModificationException();
            lastRet = cursor;
            return result;
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            //delete方法返回false时,head会加1,需要重置head,tail不变,元素向着与遍历元素相反的方向(即往数组下标最大的元素)移动,
            //为保证遍历的元素不变需要对cursor加1
            //delete方法返回true是,head不变,tail-1,元素向着与遍历元素相同的方向(即往数组下标为0的方向移动)移动,且移动的是之前遍历过的元素
            //所以cursor维持不变
            if (!delete(lastRet)) {
                cursor = (cursor + 1) & (elements.length - 1);
                fence = head;
            }
            lastRet = -1;
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_31865983/article/details/86760086