转自:
下文笔者讲述ArrayDeque简介说明
ArrayDeque简介
ArrayDeque是Deque接口的一种实现
依赖于可变数组来实现的
ArrayDeque没有容量限制,可根据需求自动进行扩容
ArrayDeque可作为栈来使用,效率要高于Stack
ArrayDeque可以作为队列来使用,效率相较于基于双向链表的LinkedList也要更好一些
ArrayDeque的成员变量
//数组存储元素 transient Object[] elements; //头部元素索引 transient int head; //尾部元素索引 transient int tail; //最小容量 private static final int MIN_INITIAL_CAPACITY = 8;
ArrayDeque底层使用数组存储元素 同时还使用head和tail来表示索引 但注意tail不是尾部元素的索引 而是尾部元素的下一位 即下一个将要被加入的元素的索引
ArrayDeque如何初始化
public ArrayDeque() { elements = new Object[16]; } public ArrayDeque(int numElements) { allocateElements(numElements); } public ArrayDeque(Collection<? extends E> c) { allocateElements(c.size()); addAll(c); } 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 = new Object[initialCapacity]; }
ArrayDeque的初始化容量必须是2^n 你传的初始化容量如果是10 那么实际申请的数组容量是16 如果申请的容量是33,那么实际的容量是62 如果申请的容量是62,那么实际申请的容量是128。 我们可以从allocateElements方法中看出以上的运行原理
ArrayDeque中add方法
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
}
public void addLast(E e) {
if (e == null)
throw new NullPointerException();
//tail中保存的是即将加入末尾的元素的索引
elements[tail] = e;
//tail向后移动一位
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
//tail和head相遇,空间用尽,需要扩容
doubleCapacity();
}
注意事项:
tail的计算公式(tail = (tail + 1) & (elements.length - 1))
当tail到达容量最后一个的时候,tail就为等于0,否则tail的值tail+1
ArrayDeque扩容机制
private void doubleCapacity() { assert head == tail; //扩容时头部索引和尾部索引肯定相等 int p = head; int n = elements.length; //头部索引到数组末端(length-1处)共有多少元素 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; }