我竟然今天才知道循环数组这个概念!
1. 怎么实现循环?
通过首尾两个下标!如果尾下标的下一个就是头下标,那么队列就满了?但是怎么知道尾下标的下一个了?可一通过下标与数组长度取余!
还有就是如果首尾相等,那么这这个队列为空!
ArrayDeque 就是通过一个循环数组实现的!它判断队列是否满了或者获得前一个元素?通过:
分析源码的过程中,我就在纳闷,ArrayDeque里面的elements数组,通过位操作进行循环数组判断时是怎么做到的!
比如说,如果elements.length - 1 = 4 , head = 2 , 那么无论怎么& 都是0 , 根本就得不到想得到的头元素啊!
通过分析它的构造函数以及结合位运算的规律,发现,elements.length - 1 根本就不可能等于4 , 8 , 16 这样的2^n ,但是elments.length 却可以为8 , 16 , 32这样2^n ,4除外,最小为8
再看看位运算的规律 : elements.length = 2 ^ n , 那么它的二进制形式,比如8 , 1000 ,那么比8小的数 , 一定是他后三位的随意组合,所以相与一定为0 ,但是对于elements.length - 1 , 比如7,
0111 , 那么,<=7 的数与7相与,不就等于本本身吗,负数相反!
1、取负数的绝对值的原码;
2、计算原码的反码;正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。
// 原码10010= 反码11101 (10010,1为符号码,故为负) (11101)
3、对反码加一,获取补码。
构造函数:
分配elements数组空间
private void allocateElements(int numElements) { //假设numElements = 10 二进制 : 1010
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); // 右移一位 0101 然后与 1010 相或 = 1111 = 15
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16); // 15 为什么要右移多次了?
initialCapacity++; // 16
if (initialCapacity < 0) // Too many elements, must back off
initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
elements = (E[]) new Object[initialCapacity];
}
public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
// 本来可以简单地写成head-1,但如果head为0,减1就变为-1了,和elements.length - 1进行与操作就是为了处理这种情况,这时结果为elements.length - 1。
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail) // head和tail不可以重叠
doubleCapacity();
}
public void addLast(E e) {
if (e == null)
throw new NullPointerException();
// tail位置是空的,把元素放到这。
elements[tail] = e;
// 和head的操作类似,为了处理临界情况 (tail为length - 1时),和length - 1进行与操作,结果为0。
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}
代码来源 : http://blog.csdn.net/microtong/article/details/4626224
/**
* 数组实现的循环队列
* @author TongQiang
*/
public class QueueArray {
Object[] a; //对象数组,队列最多存储a.length-1个对象
int front; //队首下标
int rear; //队尾下标
public QueueArray(){
this(10); //调用其它构造方法
}
public QueueArray(int size){
a = new Object[size];
front = 0;
rear =0;
}
/**
* 将一个对象追加到队列尾部
* @param obj 对象
* @return 队列满时返回false,否则返回true
*/
public boolean enqueue(Object obj){
// g关于这个地方为什么要rear+1 这样不就会少加一个元素吗?
// 如果是rear%a.length 的话,那么dang rear = 4 ,接下来,出队列,那么front = 1 , 那么这是rear % a.length = 0 . a[rear] 不就出错了吗
if((rear+1)%a.length==front){
return false;
}
a[rear]=obj;
rear = (rear+1)%a.length;
return true;
}
/**
* 队列头部的第一个对象出队
* @return 出队的对象,队列空时返回null
*/
public Object dequeue(){
if(rear==front){
return null;
}
Object obj = a[front];
front = (front+1)%a.length;
return obj;
}
public static void main(String[] args) {
QueueArray q = new QueueArray(4);
System.out.println(q.enqueue("张三"));
System.out.println(q.enqueue("李斯"));
System.out.println(q.enqueue("赵五"));
System.out.println(q.enqueue("王一"));//无法入队列,队列满
for(int i=0;i<4;i++){
System.out.println(q.dequeue());
}
}
}