队列方法基础说明见队列接口设计
一 ArrayDeque中操使用了各种位操作,先对位运算进行说明
-
二进制数表示
二进制数使用补码表示
最高位为 符号位,正数的符号为0,负数为1 。其余代表数值本身。 对于负数, 通过对该数的绝对值的补码按位取反,在对整个数加1;举例: -1的二进制表示
1的补码 00000001
取反 11111110
加1 11111111 -
位运算符号
逻辑运算符
~ 按位非& 按位与 只有两个操作数对应位同为1时,结果为1,其余全为0。
| 按位或 只有两个操作数同为0时,结果为0,其余全为1
^ 按位异或运算符;
- 相同结果为0, 否则为1. 即 0^0 = 0; 0^1 = 1 ; 1^1 = 0;
- 0 异或任何数等于任何数
- 1 异或任何数等于任何数取反
- 任何数异或自己等于把自己设置为0
移位运算
1.<< 左位移运算符 将向左移动指定位数,高位溢出截断 ; 在低位补0;
- ">>" 右移运算符,向右边移动指定的位数 ;若值为正,则在高位插入0;若值为负,则在高位插入1。 低位溢出截断
- " >>>" 右移运算符,向右边移动指定的位数 ; 无论正负,都在高位插入0
源码分析
public class ArrayDeque extends AbStractCollection implements Deque , Cloneable,Serializable{
//The capacity of the deque is the length of this array, which is always a power of two
//The array is never allowed to become full
// We also guarantee that all array cells not holding deque elements are always null.
//怎么做到分配最合适的2的幂次方数呢 见allocateElements
transient Object[] elements;
public ArrayDeque(){
element = 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;
if(numElements > initialCapacity){
//效果是把numElements数表示二进制后,在最高位1之后统统改变成1,
//由于第一位的1 表示1 在加1后 就可以得到合适的2的幂次方。
//假设传入 numElements = 18
//initialCapacity = 00010010;
initialCapacity = numElements;
//initialCapacity >>> 1 = 00001001
//initialCapacity = 00010010| 00001001 = 00011011
initialCapacity |= (initialCapacity >>> 1);
//initialCapacity >>> 2 = (00011011 >>> 2) = 00000110
//initialCapacity = 00011011 | 00000110 =00011111;
initialCapacity |= (initialCapacity >>> 2);
// initialCapacity >>> 4 = 00000001
//initialCapacity = 00011111 | 00000001 = 00011111
initialCapacity |= (initialCapacity >>> 4);
//initialCapacity = 00011111 | 00000000 = 00011111
initialCapacity |= (initialCapacity >>> 8);
//initialCapacity = 00011111 | 00000000 = 00011111
initialCapacity |= (initialCapacity >>> 16);
//initialCapacity = 00011111 = 31
initialCapacity++;
if(initialCapacity < 0){
initialCapacity >>>= 1;
}
}
elements = new Object[initialCapacity]
}
private void doubleCapacity(){
assert head == tail;
int p = head;
int n = elements.length;
int r = n - p;
//数组长度翻倍
int newCapacity = n << 1;
if (newCapacity < 0)
throw new IllegalStateException("Sorry, deque too big");
Object[] a = new Object[newCapacity];
//将head右边的元素拷贝到数组开头处
System.arraycopy(elements,p,a,0,r);
//拷贝head左边的元素到新数组里
System.arraycopy(elements,0,a,r,p);
elements = a;
head = 0;
tail = n;
}
//尾部添加
public void addLast(E e){
if(e == null){
throw ...
}
element[tail] = e;
//elements.length的二进制码 出符号位是0外 其它都是1
//假设数组长度16 elements.length = 00001111;
// tail =2
// tail = tail + 1 = 3
// 00000011 & 00001111 = 00000011 = 3 会小于数组长度,开始head=0 不需要扩容
// 假设tail =15
// tail = tail + 1 = 16
// 00010000 & 00001111 = 0 这时候就需要扩容了
if( tail = (tail + 1) &(elements.length) == head)
doubleCapacity();
}
//头部添加
public void addFirst(E e){
if (e == null)
throw new NullPointerException();
//head -1)&(elements.length -1) 计算head前一个位置
// 假设 刚开始head = 0, 数组长度16
// -1&15 = 11111111 & 00001111 = 00001111 = 15
elements[head = (head -1)&(elements.length -1)] = e;
if(head == tail){
doubleCapacity();
}
}
public E pollFirst(){
final Object[] elements = this.elements;
final int h = head;
E result = (E)elements[h];
if(result != null){
element[h] = null;
//head 移到下一个位置
head = (h+1) & (elements.length -1);
}
return result;
}
public E pollLast(){
final Object[] elements = this.elements;
//计算最后一个位置
final int t = (tail - 1) & (elements.length - 1);
E result = (E) elements[t];
if (result != null) {
elements[t] = null;
tail = t;
}
return result;
}
public boolean removeFirstOccurrence(Object o){
if(o != null){
int mask = element.length - 1;
int i = head;
//i = (i+1)&mask 相当于 i++
for(Object x; (x = elements[i]) != null; i = (i+1)&mask){
if (o.equals(x)) {
delete(i);
return true;
}
}
}
return false;
}
public boolean removeLastOccurrence(Object o) {
if (o != null) {
int mask = elements.length - 1;
int i = (tail - 1) & mask;
for (Object x; (x = elements[i]) != null; i = (i - 1) & mask) {
if (o.equals(x)) {
delete(i);
return true;
}
}
}
return false;
}
public boolean contains(Object o) {
if (o != null) {
int mask = elements.length - 1;
int i = head;
//从头部开始往后遍历, 循环到空元素就结束
for (Object x; (x = elements[i]) != null; i = (i + 1) & mask) {
if (o.equals(x))
return true;
}
}
return false;
}
public int size() {
return (tail - head) & (elements.length - 1);
}
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;
// Invariant: head <= i < tail mod circularity
if (front >= ((t - h) & mask))
throw new ConcurrentModificationException();
// Optimize for least element motion
if (front < back) {
if (h <= i) {
System.arraycopy(elements, h, elements, h + 1, front);
} else { // Wrap around
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;
} else {
if (i < t) { // Copy the null tail as well
System.arraycopy(elements, i + 1, elements, i, back);
tail = t - 1;
} else { // Wrap around
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;
}
}
}
总结:
This class is likely to be faster than {@link Stack} when used as a stack, and faster than {@link LinkedList} when used as a queue.
1.arrayDeque 基于数组实现双端队列,替代栈使用,作为队列使用效率高于linkedList。为了提高效率,设计采用了循环数组。设计了两个变量head和 tail记录了头尾。使用位操作进行各种判断,以及对head和tail的维护
2.没有索引位置的概念,如果同时需要根据索引位置进行操作,或者经常需要在中间进行插入和删除,则应该选LinkedList。 而从两端进行操作,一般而言,ArrayDeque效率更高一些,应该被优先使用,