zoukankan      html  css  js  c++  java
  • Java容器解析系列(7) ArrayDeque 详解

    ArrayDeque,从名字上就可以看出来,其是通过数组实现的双端队列,我们先来看其源码:

    /**
    有自动扩容机制;
    不是线程安全的;
    不允许添加null;
    作为栈使用时比java.util.Stack快;
    作为队列使用时比LinkedList快; 支持fast-fail;
     * @since 1.6
     */
    public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>, Cloneable, Serializable {
    
        // 元素数组;
        // 数组大小永远是2的n次方;
        // 保证所有的没有元素的位置,其值为null;
        // 关于这里的数组大小为什么要求是2的n次方,后面会具体解释
        private transient E[] elements;
        // 头指针和尾指针
        private transient int head;
        private transient int tail;
    
        // 最小容量
        private static final int MIN_INITIAL_CAPACITY = 8;
    
        // ****** Array allocation and resizing utilities ******
    
        // 找到<=指定元素的2的n次方的数作为队列容量大小,并分配数组空间
        private void allocateElements(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
            }
            elements = (E[]) new Object[initialCapacity];
        }
    
        // 将队列的容量翻倍,只在队列满时(也就是head == tail成立的时候)调用;
        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 = (E[]) a;
            head = 0;
            tail = n;
        }
        // 复制本地元素到指定数组中,供toArray()调用
        private <T> T[] copyElements(T[] a) {
            if (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;
        }
    
        // 默认大小为16
        public ArrayDeque() {
            elements = (E[]) new Object[16];
        }
        // 指定队列容量,实际队列的容量可能不是指定的数,因为队列容量必须为2的n次方
        public ArrayDeque(int numElements) {
            allocateElements(numElements);
        }
    
        public ArrayDeque(Collection<? extends E> c) {
            allocateElements(c.size());
            addAll(c);
        }
    
        // 重要的是addFirst(),addLast(),pollFirst(),pollLast()这4个方法,其他的方法都是基于这4个方法
    
        // 添加新元素到头部,头指针-1
        // 时间复杂度:O(1)
        public void addFirst(E e) {
            // 不允许添加null
            if (e == null)
                throw new NullPointerException();
            // 因为 elements.length 为 2 的n次方,表达式(head - 1) & (elements.length - 1) 与 (head - 1) % elments.length相等,且前者效率比后者高(位运算效率比取模高)
            // 这里的elements.length - 1也可以成称为掩码(mask)
            elements[head = (head - 1) & (elements.length - 1)] = e;
            // 如果添加元素导致队列满了,扩容
            if (head == tail)
                doubleCapacity();
        }
        // 添加新元素到尾部,尾指针+1
        // 时间复杂度:O(1)
        public void addLast(E e) {
            // 不允许添加null
            if (e == null)
                throw new NullPointerException();
            elements[tail] = e;
            // 因为 elements.length 为 2 的n次方,表达式(tail + 1) & (elements.length - 1) 与 (head + 1) % elments.length相等
            if ((tail = (tail + 1) & (elements.length - 1)) == head)
                // 如果添加元素导致队列满了,扩容
                doubleCapacity();
        }
    
        public boolean offerFirst(E e) {
            addFirst(e);
            return true;
        }
    
        public boolean offerLast(E e) {
            addLast(e);
            return true;
        }
    
        public E removeFirst() {
            E x = pollFirst();
            if (x == null)
                throw new NoSuchElementException();
            return x;
        }
    
        public E removeLast() {
            E x = pollLast();
            if (x == null)
                throw new NoSuchElementException();
            return x;
        }
        // 移除队头
        // 时间复杂度:O(1)
        public E pollFirst() {
            int h = head;
            E result = elements[h]; // Element is null if deque empty
            // 证明队列是空的(ArrayDeque保证所有的没有元素的位置,其值为null)
            if (result == null)
                return null;
            // 保证没有元素的地方值为null,且保证GC能正常回收
            elements[h] = null; // Must null out slot
            // 头指针右移
            head = (h + 1) & (elements.length - 1);
            return result;
        }
        // 移除队尾
        // 时间复杂度:O(1)
        public E pollLast() {
            int t = (tail - 1) & (elements.length - 1);
            E result = elements[t];
            // 队列是空的
            if (result == null)
                return null;
            elements[t] = null;
            // 尾指针左移
            tail = t;
            return result;
        }
    
        public E getFirst() {
            E x = elements[head];
            if (x == null)
                throw new NoSuchElementException();
            return x;
        }
    
        public E getLast() {
            E x = elements[(tail - 1) & (elements.length - 1)];
            if (x == null)
                throw new NoSuchElementException();
            return x;
        }
    
        public E peekFirst() {
            return elements[head]; // elements[head] is null if deque empty
        }
    
        public E peekLast() {
            return elements[(tail - 1) & (elements.length - 1)];
        }
    
        // 时间复杂度O(n)
        public boolean removeFirstOccurrence(Object o) {
            if (o == null)
                return false;
            int mask = elements.length - 1;
            int i = head;
            E x;
            while ((x = elements[i]) != null) {
                if (o.equals(x)) {
                    delete(i);
                    return true;
                }
                i = (i + 1) & mask;
            }
            return false;
        }
        // 时间复杂度O(n)
        public boolean removeLastOccurrence(Object o) {
            if (o == null)
                return false;
            int mask = elements.length - 1;
            int i = (tail - 1) & mask;
            E x;
            while ((x = elements[i]) != null) {
                if (o.equals(x)) {
                    delete(i);
                    return true;
                }
                i = (i - 1) & mask;
            }
            return false;
        }
    
        // *** Queue methods ***
    
        public boolean add(E e) {
            addLast(e);
            return true;
        }
    
        public boolean offer(E e) {
            return offerLast(e);
        }
    
        public E remove() {
            return removeFirst();
        }
    
        public E poll() {
            return pollFirst();
        }
    
        public E element() {
            return getFirst();
        }
    
        public E peek() {
            return peekFirst();
        }
    
        // *** Stack methods ***
    
        public void push(E e) {
            addFirst(e);
        }
    
        public E pop() {
            return removeFirst();
        }
    
        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;
        }
    
        private boolean delete(int i) {
            checkInvariants();
            final E[] 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;
            }
        }
    
        // *** Collection Methods ***
    
        public int size() {
            return (tail - head) & (elements.length - 1);
        }
    
        public boolean isEmpty() {
            return head == tail;
        }
    
        public Iterator<E> iterator() {
            return new DeqIterator();
        }
    
        public Iterator<E> descendingIterator() {
            return new DescendingIterator();
        }
    
        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();
                E result = elements[cursor];
                // This check doesn't catch all possible comodifications,
                // but does catch the ones that corrupt traversal
                if (tail != fence || result == null)
                    throw new ConcurrentModificationException();
                lastRet = cursor;
                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;
            }
        }
        // 反向迭代器,犹如godv的那支箭。。。
        private class DescendingIterator implements Iterator<E> {
            /*
             * This class is nearly a mirror-image of DeqIterator, using tail instead of
             * head for initial cursor, and head instead of tail for fence.
             */
            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();
                cursor = (cursor - 1) & (elements.length - 1);
                E result = elements[cursor];
                if (head != fence || result == null)
                    throw new ConcurrentModificationException();
                lastRet = cursor;
                return result;
            }
    
            public void remove() {
                if (lastRet < 0)
                    throw new IllegalStateException();
                if (!delete(lastRet)) {
                    cursor = (cursor + 1) & (elements.length - 1);
                    fence = head;
                }
                lastRet = -1;
            }
        }
    
        public boolean contains(Object o) {
            if (o == null)
                return false;
            int mask = elements.length - 1;
            int i = head;
            E x;
            while ((x = elements[i]) != null) {
                if (o.equals(x))
                    return true;
                i = (i + 1) & mask;
            }
            return false;
        }
    
        public boolean remove(Object o) {
            return removeFirstOccurrence(o);
        }
    
        public void clear() {
            int h = head;
            int t = tail;
            if (h != t) { // clear all cells
                head = tail = 0;
                int i = h;
                int mask = elements.length - 1;
                do {
                    elements[i] = null;
                    i = (i + 1) & mask;
                } while (i != t);
            }
        }
    
        public Object[] toArray() {
            return copyElements(new Object[size()]);
        }
    
        public <T> T[] toArray(T[] a) {
            int size = size();
            if (a.length < size)
                a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);
            copyElements(a);
            if (a.length > size)
                a[size] = null;
            return a;
        }
    
        // *** Object methods ***
    
        public ArrayDeque<E> clone() {
            try {
                ArrayDeque<E> result = (ArrayDeque<E>) super.clone();
                result.elements = Arrays.copyOf(elements, elements.length);
                return result;
    
            } catch (CloneNotSupportedException e) {
                throw new AssertionError();
            }
        }
    
        private static final long serialVersionUID = 2340985798034038923L;
    
        private void writeObject(ObjectOutputStream s) throws IOException {
            s.defaultWriteObject();
    
            // Write out size
            s.writeInt(size());
    
            // Write out elements in order.
            int mask = elements.length - 1;
            for (int i = head; i != tail; i = (i + 1) & mask)
                s.writeObject(elements[i]);
        }
    
        private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
            s.defaultReadObject();
    
            // Read in size and allocate array
            int size = s.readInt();
            allocateElements(size);
            head = 0;
            tail = size;
    
            // Read in all elements in the proper order.
            for (int i = 0; i < size; i++)
                elements[i] = (E) s.readObject();
        }
    }
    

    从源码可以很容易的看出来:ArrayDeque本质为数组实现的循环队列,关于循环队列,请参考博客:循环队列(顺序队列)

    增删的主要实现方法为addFirst(),addLast(),pollFirst(),pollLast()这4个方法,其他的方法都是调用这4个方法来实现功能,且其时间复杂度均为O(1)

    ArrayDeque中的数组大小必须为2的n次方,这一点的解释可以参考上述源码中addFirst()和addLast()的注释,已经说得很清楚了

    Let's go change the world,or changed by the world
  • 相关阅读:
    scott登录查询常用语句
    Oracle服务端及客户端安装
    SVN简单使用
    dos命令--查询进程
    第二周学习总结
    第一周学习总结
    虚拟机安装教程及网络连接方式的解释
    两天学习总结
    方差
    thinkphp 总结 转
  • 原文地址:https://www.cnblogs.com/jamesvoid/p/9779324.html
Copyright © 2011-2022 走看看