数组实现的双端队列,到底快不快呢

1,292 阅读8分钟

ArrayDeque

​ ArrayDeque是基于数组实现的双端队列,常用于线程池中的工作队列需要是有界队列时使用的工作队列容器。

​ 从源码描述中可知该容器不是线程安全的,在没有外部加锁的条件下,他们不能用于多线程下的并发访问。并且ArrayDeque中不允许向其中放入null元素。当该容器用做栈时他可能比Stack要高效一些。当该容器用作队列的时候他可能比LinkedList更高效一些。

声明

public class ArrayDeque<E> extends AbstractCollection<E>
                           implements Deque<E>, Cloneable, Serializable
{
	//代码
}

继承自AbstractCollection,实现了Deque接口。

成员变量

public class ArrayDeque<E> extends AbstractCollection<E>
                           implements Deque<E>, Cloneable, Serializable
{
    /**
     * ArrayDeque内部基于数组实现的双端队列。双端队列的容量就是数组的长度。
     * 该数组的长度总是2的n次方,并且不允许存在数组满的情况。
     */
    transient Object[] elements; // non-private to simplify nested class access
    
    /**
     * 双端队列中的头元素,就是即将出队列(或者出栈)的那个元素。如果双端队列为空,那么,head=tail将可能是一个
     * 任意的数字值。
     */
    transient int head;
    
    /**
     * 下一个入队(进栈)元素应该存放的下标。
     */
    transient int tail;
    
    /**
     * 一个新创建的双端队列的默认最小容量,必须是2的次方。
     */
    private static final int MIN_INITIAL_CAPACITY = 8;
    
    /**
     * 用于支持序列化机制
     */
    private static final long serialVersionUID = 2340985798034038923L;
}

构造方法——如何初始化

ArrayDeque提供了三种创建ArrayDeque的方式。分别是无参、给定初始容量、给定Collection实现类实例。从这里可以看出ArrayDeque一旦被创建,内部的数组实例就已经被初始化了。

    /**
     * 如果不穿任何参数,那么将构建一个初始容量为16的elements数组。
     */
    public ArrayDeque() 
    {
        elements = new Object[16];
    }

	/**
     * 根据传入的numElements的值初始化elements数组
     * 数组的容量是Math.max(MIN_INITIAL_CAPACITY = 8,第一个大于numElements的2的n次方)。
     * 但是不要溢出了。
     */
    public ArrayDeque(int numElements)
    {
        allocateElements(numElements);
    }

    /**
     * 根据计算后的容量创建数组。
     */
    private void allocateElements(int numElements) 
    {
        elements = new Object[calculateSize(numElements)];
    }

    /**
     * 该方法用于数组的分配和重新或者扩容的时候使用。
     */
    private static int calculateSize(int numElements) 
    {
        int initialCapacity = MIN_INITIAL_CAPACITY;
        // 要寻找到第一个大于numElements的2的n次方。
        if (numElements >= initialCapacity) {
            initialCapacity = numElements;
            initialCapacity |= (initialCapacity >>>  1);
            initialCapacity |= (initialCapacity >>>  2);
            initialCapacity |= (initialCapacity >>>  4);
            initialCapacity |= (initialCapacity >>>  8);
            initialCapacity |= (initialCapacity >>> 16);
            initialCapacity++;

            //元素值太大了,溢出了。需要减半。如果要分类2的三十次方个元素,那么
            //由于要查找大于该数的2的次方那就是2的三十一次方,这已经超过了int类型所能表示的最大范围。
            if (initialCapacity < 0)   // Too many elements, must back off
                initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
        }
        return initialCapacity;
    }
    /**
     * 根据Collection实现类c中的所有元素创建elements数组。并将其中的元素全部添加至deque中。
     * 顺序与c中迭代器返回的顺序一致。
     */
    public ArrayDeque(Collection<? extends E> c) 
    {
        allocateElements(c.size());
        addAll(c);
    }

常用方法

添加元素

addFirst方法

    /**
     * 添加元素e到deque的最前面。如果e为空,则抛出NullPointerException。
     */
    public void addFirst(E e) 
    {
        if (e == null)
            throw new NullPointerException();
        //由于要插入到最前面,那就是head-1。
        elements[head = (head - 1) & (elements.length - 1)] = e;
        //这个时候,数组中的元素已经满了,没有可用空间了,将数组容量翻倍。
        if (head == tail)
            doubleCapacity();
    }

    /**
     * 将双端队列中的数组扩容为原来的两倍。只有当数组满的时候才会执行此操作,并且head==tail是。
     * 因为ArrayDeque中的数组是循环使用的。当tail追上了head说明数组处于满的状态了。
     */
    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;
    }
/**
 * 将元素添加到栈顶,也就是队列的头部。该方法使用addFirst实现。
 */
public void push(E e) 
{
    addFirst(e);
}

offerFirst方法

    /**
     * 将元素入队列到队列的最前面的元素。该方法与addFirst一样,只不过该方法添加成功返回true。
     */
    public boolean offerFirst(E e) 
    {
        addFirst(e);
        return true;
    }

addLast方法

    /**
     * 添加元素e到deque的最后面。如果e为空,则抛出NullPointerException。
     */
    public void addLast(E e) 
    {
        if (e == null)
            throw new NullPointerException();
        //tail位置就是下一个将要从后面入队的元素。
        elements[tail] = e;
        //这时候队列已经满了,内部的数组已经没有空间了。马上扩容。
        if ( (tail = (tail + 1) & (elements.length - 1)) == head)
            doubleCapacity();
    }

offerLast方法

    /**
     * 将元素入队列到队列的最后面的元素。该方法与addLast一样,只不过该方法添加成功返回true。
     */
    public boolean offerLast(E e)
    {
        addLast(e);
        return true;
    }

add方法

/**
 * 将元素添加到队列的尾部,使用addLast实现,新瓶装旧酒。
 */
public boolean add(E e)
{
    addLast(e);
    return true;
}

offer方法

/**
 * 将元素添加到队列的尾部,使用offerLast实现,新瓶装旧酒。
 */
public boolean offer(E e)
{
    return offerLast(e);
}

删除元素

pollFirst方法

    /**
     * 将head处元素出队列并返回该元素,并将head处的元素置为空。
     * 这里可以理解为什么ArrayDeque不能存放null元素了。因为这样就
     * 不能区分一个位置是空闲的还是存放了null元素。
     * 另外这里的取模操作也转化为与数组长度相与的操作,这里数组的长度必须是2的n次方才可以
     * 这样代替取模操作。
     */
	public E pollFirst() 
    {
        int h = head;
        @SuppressWarnings("unchecked")
        E result = (E) elements[h];
        // 如果队列为空,则返回null.
        if (result == null)
            return null;
        elements[h] = null;     // Must null out slot
        head = (h + 1) & (elements.length - 1);
        return result;
    }

poll方法

/**
 * 将队列中最前面的元素出队并且返回元素。使用pollFirst实现。
*/
public E poll() 
{
    return pollFirst();
}

pollLastf方法

    /**
     * 将tail-1处元素出队列并返回该元素,并将tail-1处的元素置为空。
     * 这里可以理解为什么ArrayDeque不能存放null元素了。因为这样就
     * 不能区分一个位置是空闲的还是存放了null元素。
     * 另外这里的取模操作也转化为与数组长度相与的操作,这里数组的长度必须是2的n次方才可以
     * 这样代替取模操作。
     */
	public E pollLast()
    {
        //tail是下一个要从尾部加入的元素的位置。所以tail-1才是最后一个元素的位置。
        int t = (tail - 1) & (elements.length - 1);
        @SuppressWarnings("unchecked")
        E result = (E) elements[t];
        if (result == null)
            return null;
        elements[t] = null;
        tail = t;
        return result;
    }

removeFirst方法

    /**
     * 将队列中最前面的元素出队并且返回元素。该方法与pollFirst基本一致,只不过当元素为空时该方法
     * 将抛出NoSuchElementException
     */
    public E removeFirst() 
    {
        E x = pollFirst();
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }

remove方法

/**
 * 将队列中最前面的元素出队并且返回元素。使用removeFirst实现。
*/
public E remove() 
{
    return removeFirst();
}
/**
 * 将栈顶元素删除并弹出返回,也就是删除并返回头部的元素。使用removeFirst实现。
 */
public E pop() {
    return removeFirst();
}

removeLast方法

    /**
     * 将队列中最后面的元素出队并且返回元素。该方法与pollLast基本一致,只不过当元素为空时该方法
     * 将抛出NoSuchElementException
     */
    public E removeLast() 
    {
        E x = pollLast();
        if (x == null)
            throw new NoSuchElementException();
        return x;
    }

delete方法

/**
 * 删除数组中特定元素的的下标位置处的元素。这将导致head以及tail的调整,
 * 同时会有一部分元素想前或者向后移动。
 * 向前移动的情况很好理解,数组正常的head<tail的时候,删除元素会导致后面的元素前移。
 * 向后移动元素则是head>tail的时候,如果删除了尾部的元素。那么将导致前面的元素虽然在位置上是排在前面的但是
 * 在队列中是排在后面的,所以要向后移动。
 */
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;

    // 这个i不在head到tail管辖的范围之内。
    if (front >= ((t - h) & mask))
        throw new ConcurrentModificationException();
    
	 //寻找移动元素数量最少的方法。前移还是后移。
    // front和back表示要删除的元素离head更近还是离tail更近。
	//这种情况是离head更近。
    if (front < back) 
    {
         
    	// 0 0 x 0 0 0 0 0 0
    	// h               t
    	//这种情况是head在前,tail在后,并且要删除的元素离head更近
        if (h <= i) 
        {
            //前面的元素后移一位。
            System.arraycopy(elements, h, elements, h + 1, front);
        }
        else
        { 	// Wrap around
            // 0 0 0 0 0 0 0 0  0
    		//   t       x   h      
            //这种情况是这种情况是head在后,tail在前,并且要删除的元素离head更近,并且x在h的前面。
            //那么首先将x之前的元素后移,然后让最后的元素前移到0位置。
           	//然后将h处的元素后移一位。
            //这种方式是循环后移。
            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;
    }
    //这种情况是离tail更近。
    else
    {
        //如果要删除的元素在tail的前面
        // 0 0 0 0 0 0 0 0 0
    	// h           x   t
        if (i < t) 
        { // Copy the null tail as well
            //把x之后的元素到tail之前的元素前移即可了。
            System.arraycopy(elements, i + 1, elements, i, back);
            tail = t - 1;
        }
        //如果要删除的元素在tail的后面
        // 0 0 0 0 0 0 0 0 0 0 0
    	// t   h             x   
        else 
        { // Wrap around
            //那么将x后面的元素前移一位
           	//然后将0处的元素移到末尾。
            //然后将从1开始到t之前的元素前移一位。
            //这种方式是循环前移。
            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;
    }
}


/**
 * 检查项:
 * 1、tail处必须为null。
 * 2、队列为空或者队列至少有一个元素且队列不满。
 */
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;
}

removeFirstOccurrence方法

/**
 * 从head(第一个元素开始向后遍历)直到找到与传入o相等的元素并根据此时的下标删除该元素。
 * 删除的逻辑委托为内部的delete方法实现。
 * 如果传入的元素o为null,直接不做任何操作返回false。
 * 如果没有找到与之相匹配的也返回false。
 */
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;
}

remove方法

/**
 * 从head(第一个元素开始向后遍历)直到找到与传入o相等的元素并根据此时的下标删除该元素。
 * 删除的逻辑委托为内部的removeFirstOccurrence方法实现。
 * 如果传入的元素o为null,直接不做任何操作返回false。
 * 如果没有找到与之相匹配的也返回false。
 */
public boolean remove(Object o) {
    return removeFirstOccurrence(o);
}

removeLastOccurrence方法

/**
 * 从tail-1(最后一个元素开始向前遍历)直到找到与传入o相等的元素并根据此时的下标删除该元素。
 * 删除的逻辑委托为内部的delete方法实现。
 * 如果传入的元素o为null,直接不做任何操作返回false。
 * 如果没有找到与之相匹配的也返回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;
}
/**
 * 删除队列中的所有的元素,该方法返回后队列将变为空。
 */
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);
    }
}

获取元素

getFirst方法

/**
 * 只是获取head处的元素的值。不对队列做操作。如果对应处元素为null,则抛出异常
 */
public E getFirst() 
{
    @SuppressWarnings("unchecked")
    E result = (E) elements[head];
    if (result == null)
        throw new NoSuchElementException();
    return result;
}

element方法

/**
 * 只是获取head处的元素的值。不对队列做操作。如果对应处元素为null,则抛出异常
 * 该方法与getFirst,peek,peekFirst方法类似,但是peek系的方法不会抛出异常。
 */
public E element() 
{
    return getFirst();
}

getLast方法

/**
 * 只是获取tail-1处的元素的值。不对队列做操作。如果对应处元素为null,则抛出异常
 */
public E getLast()
{
    @SuppressWarnings("unchecked")
    E result = (E) elements[(tail - 1) & (elements.length - 1)];
    if (result == null)
        throw new NoSuchElementException();
    return result;
}

peekFirst方法

/**
 * 只是获取head处的元素的值。不对队列做操作。如果队列为空,则返回null。
 */
@SuppressWarnings("unchecked")
public E peekFirst() 
{
    // elements[head] is null if deque empty
    return (E) elements[head];
}
/**
 * 只是获取head处的元素的值。不对队列做操作。如果队列为空,则返回null。该方法使用peekFirst实现。
 */
public E peek()
{
    return peekFirst();
}

peekLast方法

/**
 * 只是获取tail-1处的元素的值。不对队列做操作。如果队列为空,则返回null。
 */
@SuppressWarnings("unchecked")
public E peekLast() 
{
    return (E) elements[(tail - 1) & (elements.length - 1)];
}

查询信息

size方法

/**
 * 返回队列中的元素的数量。
 * 一个负数与一个2的n次方-1相与相当于2的n次方减(这个负数的绝对值然后取模2的n次方)。
 */
public int size() 
{
    return (tail - head) & (elements.length - 1);
}

isEmpty方法

/**
 * 查看队列是否为空,当head与tail相同时为空。
 */
public boolean isEmpty() 
{
    return head == tail;
}

contains方法

/**
 * 如果队列中从head到tail包含该元素o,则返回true,否则返回false。
 * 注意如果传入的元素为null,那么直接返回false。
 */
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;
}

ArrayDeque内部的迭代器实现

获取迭代器

iterator方法

/**
 * 获取ArrayDeque实现的Iterator实现类DeqIterator的实例。用于
 * 遍历元素。元素的顺序是从head到tail的顺序。
 * 该顺序与元素出队列的顺序一致。
 */
public Iterator<E> iterator() 
{
    return new DeqIterator();
}

descendingIterator方法

/**
 * 获取ArrayDeque实现的Iterator实现类DescendingIterator的实例。用于
 * 遍历元素。元素的顺序是从tail-1到head的顺序。
 * 该顺序与元素出队列的顺序相反。
 */
public Iterator<E> descendingIterator() 
{
    return new DescendingIterator();
}

Iterator实现类——DeqIterator

private class DeqIterator implements Iterator<E> 
{
    /**
     * 下一个调用next方法应该返回的元素的位置。
     */
    private int cursor = head;

    /**
     * tail用来停止迭代器,因为遍历到tail也就代表达到了队列的尾部。
     * 同时tail在这里充当了modCount的角色。但是相比起modCount来
     * 这个变量要逊色很多,他并不能检测到任何可能的篡改,只能规范遍历的规则。
     */
    private int fence = tail;

    /**
     * 上次调用next方法返回的元素的元素的位置,如何调用了remove方法,那么
     * 该方法将被置为-1,以拒绝后续的remove操作。
     */
    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];
        // 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;
    }

    //该方法内部是通过delete方法删除lastRet位置的元素。
    public void remove() 
    {
        if (lastRet < 0)
            throw new IllegalStateException();
        if (delete(lastRet))
        { // 返回true代表删除操作是通过循环左移的。
            cursor = (cursor - 1) & (elements.length - 1);
            fence = tail;
        }
        lastRet = -1;
        //后续不能再次马上调用remove方法了。
    }
	//从cursor到fence处的元素将交给Consumer来消费。
    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);
        }
    }
}

Iterator实现类——DescendingIterator

private class DescendingIterator implements Iterator<E> 
{
    /*
     * 该类几乎是DeqIterator的镜像类。使用tail来作为初始的cursor,使用head来作为fence.
     */
    private int cursor = tail;
    private int fence = head;
    private int lastRet = -1;

    public boolean hasNext() 
    {
        return cursor != fence;
    }
	//这个时候cursor要递减。
    public E next() 
    {
        if (cursor == fence)
            throw new NoSuchElementException();
        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();
        if (!delete(lastRet))
        {
            //返回false是代表循环后移的。
            cursor = (cursor + 1) & (elements.length - 1);
            fence = head;
        }
        lastRet = -1;
    }
}

ArrayDeque内部的序列化规则

writeObject方法

/**
 * 保存当前队列到流中。
 */
private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.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]);
}

其他方法

copyElements方法

/**
 * 将Deque内部的数组中的元素复制到给定的数组a中,这里并没有检查数组a是否可以容纳下队列
 * 中的所有的元素,而是假设可以容纳。因为该方法是private的,只能内部使用。
 * 如果head<tail的情况,那么数组中的元素就是[head,tail)。直接复制就可以了
 * 如果head>tail,那么就需要复制两次。一次是[head,elements.length-1]。
 * 另一次是[0,tail)。
 */
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;
}

    /**
     * 该方法首先创建一个当前队列中元素数量大小的数组,然后将队列中的元素复制到数组中,,由于是新创建的数组
     * 因为返回给调用者之后,调用者可以随意的修改。但是
     * 该复制也还是浅复制,就是如何修改内部元素的属性,还是会反映到队列内部。
     */

    public Object[] toArray()
    {
        return copyElements(new Object[size()]);
    }

    /**
     * 该方法通过传入一个泛型类型的数组,如果队列中的元素数量小于传入的数组的容量,那么
     * 就将队列中的元素复制到传入的数组中,此时如果数组中还有剩余空间,那么数组中第size(队列中的元素的数量)
     * 设置为null,以供调用者区分(前提是调用者确定集合中不存在非空元素)。
     * 如果传入的数组不能装下队列中的所有元素,那么就新创建一个与传入数组相同类型的新数组,容量为size。
     *并将队列中的元素复制到新数组中返回。
     */
    @SuppressWarnings("unchecked")
    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;
    }

    /**
     * 返回当前队列的一个拷贝,这个拷贝是浅拷贝。
     */
    public ArrayDeque<E> clone() 
    {
        try 
        {
            @SuppressWarnings("unchecked")
            ArrayDeque<E> result = (ArrayDeque<E>) super.clone();
            result.elements = Arrays.copyOf(elements, elements.length);
            return result;
        } 
        catch (CloneNotSupportedException e) 
        {
            throw new AssertionError();
        }
    }

readObject方法

/**
 * 从流中重建队列。
 */
private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException 
{
    s.defaultReadObject();

    // Read in size and allocate array
    int size = s.readInt();
    int capacity = calculateSize(size);
    SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
    allocateElements(size);
    head = 0;
    tail = size;

    // Read in all elements in the proper order.
    for (int i = 0; i < size; i++)
        elements[i] = s.readObject();
}

java8新增api

/**
 * Creates a <em><a href="Spliterator.html#binding">late-binding</a></em>
 * and <em>fail-fast</em> {@link Spliterator} over the elements in this
 * deque.
 *
 * <p>The {@code Spliterator} reports {@link Spliterator#SIZED},
 * {@link Spliterator#SUBSIZED}, {@link Spliterator#ORDERED}, and
 * {@link Spliterator#NONNULL}.  Overriding implementations should document
 * the reporting of additional characteristic values.
 *
 * @return a {@code Spliterator} over the elements in this deque
 * @since 1.8
 */
public Spliterator<E> spliterator() 
{
    return new DeqSpliterator<E>(this, -1, -1);
}

static final class DeqSpliterator<E> implements Spliterator<E> 
{
    private final ArrayDeque<E> deq;
    private int fence;  // -1 until first use
    private int index;  // current index, modified on traverse/split

    /** Creates new spliterator covering the given array and range */
    DeqSpliterator(ArrayDeque<E> deq, int origin, int fence) 
    {
        this.deq = deq;
        this.index = origin;
        this.fence = fence;
    }

    private int getFence() 
    { // force initialization
        int t;
        if ((t = fence) < 0) 
        {
            t = fence = deq.tail;
            index = deq.head;
        }
        return t;
    }

    public DeqSpliterator<E> trySplit() 
    {
        int t = getFence(), h = index, n = deq.elements.length;
        if (h != t && ((h + 1) & (n - 1)) != t) 
        {
            if (h > t)
                t += n;
            int m = ((h + t) >>> 1) & (n - 1);
            return new DeqSpliterator<>(deq, h, index = m);
        }
        return null;
    }

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

    public boolean tryAdvance(Consumer<? super E> consumer) 
    {
        if (consumer == null)
            throw new NullPointerException();
        Object[] a = deq.elements;
        int m = a.length - 1, f = getFence(), i = index;
        if (i != fence) 
        {
            @SuppressWarnings("unchecked") E e = (E)a[i];
            index = (i + 1) & m;
            if (e == null)
                throw new ConcurrentModificationException();
            consumer.accept(e);
            return true;
        }
        return false;
    }

    public long estimateSize()
    {
        int n = getFence() - index;
        if (n < 0)
            n += deq.elements.length;
        return (long) n;
    }

    @Override
    public int characteristics() 
    {
        return Spliterator.ORDERED | Spliterator.SIZED |
            Spliterator.NONNULL | Spliterator.SUBSIZED;
    }
}

几个注意

  1. 所有的remove相关的api都会抛出异常(NoSuchElementException),而poll相关api在队列为空时将返回null。
  2. 所有的get相关的api都可能会抛出异常(NoSuchElementException),而peek相关api在队列为空时返回null。
  3. ArrayDeque是双端队列,因此前后都可以操作数组,而ArrayDeque对于栈的相关api的实现是通过操作队列最前面的数据元素实现的。并且相关的出栈操作使用的相应的remove相关api,也就是可能会抛出相关异常。
  4. 除了创建时刻会初始化数组,队列中只有addFirst和addLast方法会引起队列内部数组容量的翻倍。翻倍的条件是head==tail