ArrayDeque解析

ArrayDeque是一个双向队列,既可以实现栈,也能用作队列。内部通过数组来实现,能够同时在两端进行插入删除的操作,同时也是非线程安全的,如果在多线程中使用它,需要自己去处理同步操作,它不允许放入null元素。用作栈时,优于Stack,用作队列时,效率高于LinkedList.

构造函数:
public ArrayDeque() {
//默认初始化一个长度为16的数组
elements = new Object[16];
}
传参数时则根据参数来初始化数组,或者也可以传入一个Collection用于构造队列。

看看ArrayDeque的基本方法和源码:

从尾部添加元素:
* boolean add(E e): return true,throws NullPointerException
* void addLast(E e):throws NullPointerException,等同add()
* boolean offer(E e): return true,throws NullPointerException
* boolean offerLast(E e):return true, throws NullPointerException

上面的几个方法其实内部都是调用了addLast(),本质是一样的。只是addLast没有返回值。
 #看看addLast()的源码:
 public void addLast(E e) {
     //不允许插入空元素
    if (e == null)
        throw new NullPointerException();
    //tail是尾部元素下一个null元素的index,直接将e赋给这个索引的元素。
    elements[tail] = e;
    //插入之后tail往后移动一位, tail =(tail + 1) & (elements.length -1) :因为elements是2的倍数,所以elements.length -1 低位全是1.那么(tail + 1) & (elements.length -1)在前者比后者小或等于后者时,结果等于tail +1.但是,当tail +1 超过(elements.length -1)时,即tail +1 = elements.length时,再& ,结果为0,即tail  ==0,tail回到初始值。
    /若tail=head说明数组满了。进行扩容。
    if ( (tail = (tail + 1) & (elements.length - 1)) == head)
        doubleCapacity();
}
 //让ArrayDeque的容量增大一倍。当ArrayDeque容量满了的时候,即head == tail时
private void doubleCapacity() {
    assert head == tail;
    int p = head; 
    int n = elements.length;  
    int r = n - p;  //head右边的长度
    int newCapacity = n << 1;    //容量*2
    if (newCapacity < 0) //超出了int限制
        throw new IllegalStateException("Sorry, deque too big");
    Object[] a = new Object[newCapacity];

/**
 * @param      src        要进行复制的数组
 * @param      srcPos     开始复制的位置
 * @param      dest        目标数组
 * @param      destPos    目标数组开始复制的位置
 * @param      length     源数组要复制的长度
 * arraycopy(Object src,  int  srcPos, Object dest, int destPos,int length);     
 */
    System.arraycopy(elements, p, a, 0, r);
    System.arraycopy(elements, 0, a, r, p);  
    //首先将head右边的元素复制到新的数组中,然后将0到head的元素复制追加到新数组后面。
    elements = a;
    head = 0;
    tail = n;  //因为 n = 旧的数组长度,即是新的数组下一个可以插入元素的位置
}

从头部添加元素:
* void addFirst(E e ): throws NullPointerException
* boolean offerFirst(E e): return true, throws NullPointerException
* void push(E e): 等同于addFirst()

addFirst()[push()] 和offerFirst()唯一不同之处在于offerFirst返回值为true,而另外两个为void。

看看addFirst(E e)的源码:
  public void addFirst(E e) {
    if (e == null)
        throw new NullPointerException();
    //head ==0 时,head -1 =-1,负数在计算机中以补码表示,此时(head - 1)& (elements.length - 1)结果其实就是(elements.length - 1)。
    //可以看到当head =0 时,从头添加其实就是从数组最大位置开始添加,这也证实了数组是循环数组。当head > 0时,从head的左边往回添加。把这个数组想象成一个圆环。
    elements[head = (head - 1) & (elements.length - 1)] = e;
    //如果head和tail相等,即说明下一个插入的位置就在head了,即是数组已经循环了一圈,所以进行扩容。
    if (head == tail)
        doubleCapacity();
}

从头部移除元素:
* E poll() :等同于pollFirst()
* E pollFirst():
* E pop() : throws NoSuchElementException
* E remove() : throws NoSuchElementException,等于removeFirst()
* E removeFirst() :throws NoSuchElementException

1、2等价,3、4、5等价,1、2和3、4、5之间的区别也在于是否抛出异常。其实内部都是通过pollFirst()来移除。

public E pollFirst() {
    final Object[] elements = this.elements;
    final int h = head;
    @SuppressWarnings("unchecked")
    E result = (E) elements[h];
   //直接定位到head索引位置,将该位置置空,此时head要后移一位,因为原本该位置已经为null.
    if (result != null) {
        elements[h] = null; 
        //保证数组下标不越界
        head = (h + 1) & (elements.length - 1);
    }
    return result;
}

从尾部移除元素:
* E pollLast():
* E removeLast(): throws NoSuchElementException

//将tail -1 索引位置的元素删除即可。tail往回移一位。
 public E pollLast() {
    final Object[] elements = this.elements;
    final int t = (tail - 1) & (elements.length - 1);
    @SuppressWarnings("unchecked")
    E result = (E) elements[t];
    if (result != null) {
        elements[t] = null;
        tail = t;
    }
    return result;
}

移除指定元素以及指定顺序的元素:
* boolean remove(Object o) ,等价于removeFirstOccurrence(Object o)
* boolean removeFirstOccurrence(Object o):从头往尾部检索,将第一次出现的object对象移除
* boolean removeLastOccurrence(Object o) : 从头部往尾部检索,将最后出现的该object对象移除

获取指定位置元素,直接通过head和tail索引来获取头部或者尾部元素即可。
 * E element():获取队列的头部元素,等价于getFirst(), throws NoSuchElementException
 * getFirst(): 获取头部元素,throws NoSuchElementException
 * getLast():获取尾部元素,throws  NoSuchElementException
 * peek():获取头部元素,等价于peekFirst()  
 * peekFirst()
 * peekLast():获取尾部元素

 获取ArrayDeque的迭代器:
 * Iterator<E> descendingIterator():返回逆序的队列迭代器,从tail开始往回读取数据
 * Iterator<E> iterator():返回正常顺序的迭代器,从head开始往后读取数据

猜你喜欢

转载自blog.csdn.net/qq_26984087/article/details/78286303