标签:
1 public class ArrayDeque<E> extends AbstractCollection<E> 2 implements Deque<E>, Cloneable, Serializable
1 public interface Queue<E> extends Collection<E> { 2 // 增加一个元素到队尾,如果队列已满,则抛出一个IIIegaISlabEepeplian异常 3 boolean add(E e); 4 // 添加一个元素到队尾并返回true,如果队列已满,则返回false 5 boolean offer(E e); 6 // 移除并返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常 7 E remove(); 8 // 移除并返问队列头部的元素,如果队列为空,则返回null 9 E poll(); 10 // 返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常 11 E element(); 12 // 返问队列头部的元素,如果队列为空,则返回null 13 E peek(); 14 }
1 // 底层用数组存储元素 2 private transient E[] elements; 3 // 队列的头部元素索引(即将pop出的一个) 4 private transient int head; 5 // 队列下一个要添加的元素索引 6 private transient int tail; 7 // 最小的初始化容量大小,需要为2的n次幂 8 private static final int MIN_INITIAL_CAPACITY = 8;
1 /** 2 * 默认构造方法,数组的初始容量为16 3 */ 4 public ArrayDeque() { 5 elements = (E[]) new Object[16]; 6 } 7 8 /** 9 * 使用一个指定的初始容量构造一个ArrayDeque 10 */ 11 public ArrayDeque( int numElements) { 12 allocateElements(numElements); 13 } 14 15 /** 16 * 构造一个指定Collection集合参数的ArrayDeque 17 */ 18 public ArrayDeque(Collection<? extends E> c) { 19 allocateElements(c.size()); 20 addAll(c); 21 } 22 23 /** 24 * 分配合适容量大小的数组,确保初始容量是大于指定numElements的最小的2的n次幂 25 */ 26 private void allocateElements(int numElements) { 27 int initialCapacity = MIN_INITIAL_CAPACITY; 28 // 找到大于指定容量的最小的2的n次幂 29 // Find the best power of two to hold elements. 30 // Tests "<=" because arrays aren‘t kept full. 31 // 如果指定的容量小于初始容量8,则执行一下if中的逻辑操作 32 if (numElements >= initialCapacity) { 33 initialCapacity = numElements; 34 initialCapacity |= (initialCapacity >>> 1); 35 initialCapacity |= (initialCapacity >>> 2); 36 initialCapacity |= (initialCapacity >>> 4); 37 initialCapacity |= (initialCapacity >>> 8); 38 initialCapacity |= (initialCapacity >>> 16); 39 initialCapacity++; 40 41 if (initialCapacity < 0) // Too many elements, must back off 42 initialCapacity >>>= 1; // Good luck allocating 2 ^ 30 elements 43 } 44 elements = (E[]) new Object[initialCapacity]; 45 }
2^1 = 10 2^2 = 100 2^3 = 1000 2^n = 1(n个0)
再来看下2^n-1的二进制是什么样子的:
2^1 - 1 = 01 2^2 - 1 = 011 2^3 - 1 = 0111 2^n - 1 = 0(n个1)
1 // 确保容量为2的n次幂,是capacity为大于initialCapacity的最小的2的n次幂 2 int capacity = 1; 3 while (capacity < initialCapacity) 4 capacity <<= 1;
那么这两种方法有什么区别呢?HashMap中的这种写法更容量理解,而ArrayDeque中的效果更高(最多经过4次位移和或操作+1次加一操作)。
4.入队(添加元素到队尾)
1 /** 2 * 增加一个元素,如果队列已满,则抛出一个IIIegaISlabEepeplian异常 3 */ 4 public boolean add(E e) { 5 // 调用addLast方法,将元素添加到队尾 6 addLast(e); 7 return true; 8 } 9 10 /** 11 * 添加一个元素 12 */ 13 public boolean offer(E e) { 14 // 调用offerLast方法,将元素添加到队尾 15 return offerLast(e); 16 } 17 18 /** 19 * 在队尾添加一个元素 20 */ 21 public boolean offerLast(E e) { 22 // 调用addLast方法,将元素添加到队尾 23 addLast(e); 24 return true; 25 } 26 27 /** 28 * 将元素添加到队尾 29 */ 30 public void addLast(E e) { 31 // 如果元素为null,咋抛出空指针异常 32 if (e == null) 33 throw new NullPointerException(); 34 // 将元素e放到数组的tail位置 35 elements[tail ] = e; 36 // 判断tail和head是否相等,如果相等则对数组进行扩容 37 if ( (tail = (tail + 1) & ( elements.length - 1)) == head) 38 // 进行两倍扩容 39 doubleCapacity(); 40 }
这里,( (tail = (tail + 1) & ( elements.length - 1)) == head)这句代码是关键,为什么会这样写呢。正常的添加元素后应该是将tail+1对不对,但是队列的删除和添加是不在同一端的,什么意思呢,我们画个图看一下。
2^1 = 10 2^2 = 100 2^3 = 1000 2^n = 1(n个0)
再来看下2^n-1的二进制是什么样子的:
2^1 - 1 = 01 2^2 - 1 = 011 2^3 - 1 = 0111 2^n - 1 = 0(n个1)
1 /** 2 * 数组将要满了的时候(tail==head)将,数组进行2倍扩容 3 */ 4 private void doubleCapacity() { 5 // 验证head和tail是否相等 6 assert head == tail; 7 int p = head ; 8 // 记录数组的长度 9 int n = elements .length; 10 // 计算head后面的元素个数,这里没有采用jdk中自带的英文注释right,是因为所谓队列的上下左右,只是我们看的方位不同而已,如果上面画的图,这里就应该是left而非right 11 int r = n - p; // number of elements to the right of p 12 // 将数组长度扩大2倍 13 int newCapacity = n << 1; 14 // 如果此时长度小于0,则抛出IllegalStateException异常,什么时候newCapacity会小于0呢,前面我们说过了int值<<1越界 15 if (newCapacity < 0) 16 throw new IllegalStateException( "Sorry, deque too big" ); 17 // 创建一个长度是原数组大小2倍的新数组 18 Object[] a = new Object[newCapacity]; 19 // 将原数组head后的元素都拷贝值新数组 20 System. arraycopy(elements, p, a, 0, r); 21 // 将原数组head前的元素都拷贝到新数组 22 System. arraycopy(elements, 0, a, r, p); 23 // 将新数组赋值给elements 24 elements = (E[])a; 25 // 重置head为数组的第一个位置索引0 26 head = 0; 27 // 重置tail为数组的最后一个位置索引+1((length - 1) + 1) 28 tail = n; 29 }
1 /** 2 * 移除并返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常 3 */ 4 public E remove() { 5 // 调用removeFirst方法,移除队头的元素 6 return removeFirst(); 7 } 8 9 /** 10 * @throws NoSuchElementException {@inheritDoc} 11 */ 12 public E removeFirst() { 13 // 调用pollFirst方法,移除并返回队头的元素 14 E x = pollFirst(); 15 // 如果队列为空,则抛出NoSuchElementException异常 16 if (x == null) 17 throw new NoSuchElementException(); 18 return x; 19 } 20 21 /** 22 * 移除并返问队列头部的元素,如果队列为空,则返回null 23 */ 24 public E poll() { 25 // 调用pollFirst方法,移除并返回队头的元素 26 return pollFirst(); 27 } 28 29 public E pollFirst() { 30 int h = head ; 31 // 取出数组队头位置的元素 32 E result = elements[h]; // Element is null if deque empty 33 // 如果数组队头位置没有元素,则返回null值 34 if (result == null) 35 return null; 36 // 将数组队头位置置空,也就是删除元素 37 elements[h] = null; // Must null out slot 38 // 将head指针往前移动一个位置 39 head = (h + 1) & (elements .length - 1); 40 // 将队头元素返回 41 return result; 42 }
1 /** 2 * 返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常 3 */ 4 public E element() { 5 // 调用getFirst方法,获取队头的元素 6 return getFirst(); 7 } 8 9 /** 10 * @throws NoSuchElementException {@inheritDoc} 11 */ 12 public E getFirst() { 13 // 取得数组head位置的元素 14 E x = elements[head ]; 15 // 如果数组head位置的元素为null,则抛出异常 16 if (x == null) 17 throw new NoSuchElementException(); 18 return x; 19 } 20 21 /** 22 * 返回队列头部的元素,如果队列为空,则返回null 23 */ 24 public E peek() { 25 // 调用peekFirst方法,获取队头的元素 26 return peekFirst(); 27 } 28 29 public E peekFirst() { 30 // 取得数组head位置的元素并返回 31 return elements [head]; // elements[head] is null if deque empty 32 }
标签:
原文地址:http://www.cnblogs.com/vn2019/p/5174010.html