类介绍
ArrayDeque类是Deque接口的一个可变大小的数组实现,我们通常称之为双端队列。该类具有以下一些特点:
- 该队列没有容量限制,可以根据使用需要自动增长;
- 该队列不是线程安全的;
- 不允许存储null;
- 该队列作为一个栈来使用比stack快,作为一个队列使用比linkedList快。
- 该双端队列的iterator方法返回的迭代器
基本使用
public class ArrayDequeTest {
public static void main(String[] args) {
Deque<String> queue = new ArrayDeque<>();
//添加元素
queue.addFirst("first element");
queue.addLast("last element");
queue.offerFirst("offerfirst element"); //成功返回true
queue.offerLast("offerlast element"); //成功返回true
queue.addFirst("first element");
queue.addFirst("first element");
queue.addFirst("first element");
// int index=0,size=queue.size();
// while (index++<size){
// System.out.println(queue.removeFirst());
// }
//删除元素
System.out.println(queue.pollFirst());
System.out.println(queue.pollLast());
System.out.println(queue.removeFirst());
System.out.println(queue.removeLast());
System.out.println(queue.removeFirstOccurrence("first element"));
System.out.println(queue.removeLastOccurrence("first element"));
/**输出:
* first element
* offerlast element
* first element
* last element
* true
* true
* */
//仅仅获取元素,若无元素,会抛出异常
System.out.println(queue.getFirst());
System.out.println(queue.getFirst());
System.out.println(queue.getLast());
System.out.println(queue.peekFirst());
System.out.println(queue.peekLast());
/**
* offerfirst element
* offerfirst element
* offerfirst element
* */
//队列操作
queue.clear();
System.out.println(queue.size());
queue.add("add element");
System.out.println(queue.offer("offer element"));
System.out.println(queue.element()); //若队列为空,则抛出异常
System.out.println(queue.remove()); //若队列为空,则抛出异常
System.out.println(queue.poll()); //若队列为空,则返回nul
System.out.println(queue.peek()); //若队列为空,则返回null
/**
* 0
* true
* add element
* add element
* offer element
* null
* */
//栈操作
queue.push("push element");
System.out.println(queue.pop()); //若栈为空,则抛出异常
//输出: push element
// queue.addFirst(null); //输出:Exception in thread "main" java.lang.NullPointerException
// queue.addLast(null); //输出:Exception in thread "main" java.lang.NullPointerException
}
}
关键源码分析
allocateElements方法
该方法在类构造函数会使用到,其作用是根据给定的入参numElements,生成刚好比numElements大的2的n次幂数。其中在
initialCapacity++之前,initialCapacity被转换成了一个有效数字全部为1的二进制数,然后给initialCapacity加1,恰好就得到了一个2的n次幂数值。用该数值作为双端队列elements数组的初始值,在后续的操作中非常有用。
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];
}
addFirst方法
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
}
通过allocateElements方法,我们可以保证任何时刻elements.length都可以表示为2^n(n为正整数),因此,element.length-1的二进制表示为低n为全部为1,高位全部为0的数。
对于result =(head-1)& (elements.length - 1):
如果0 <= head-1 < element.length,那么result=head-1。
如果head-1 = -1,那么将-1的补码与elements.length - -1 相与的结果是elements.lenght。
综上所述,head-1不可能小于-1,大于或者等于elements.length。并且,head是循环的。
如果,程序想要向ArrayDeque中添加一个null值,程序会抛出一个空指针异常。
addLast方法
public void addLast(E e) {
if (e == null)
throw new NullPointerException();
elements[tail] = e;
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}
同addFirst方法,由于elements.lenth-1的特殊性, (tail + 1) & (elements.length - 1))永远在0-elements.length - 1之间循环,不会发生越界。
总结
ArrayDeque是在jdk1.6之后出现的,可以作为栈使用,也可以作为队列使用。