我竟然今天才知道循环数组这个概念!
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、对反码加一,获取补码。
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()); } } }