ArrayDeque循环队列的部分源码分析
来源:互联网 发布:手机桌面软件 编辑:程序博客网 时间:2024/06/04 19:34
ArrayDeque是一个以循环数组实现的双向队列。
官方是这么描述的:Deque 接口的大小可变数组的实现。数组双端队列没有容量限制;它们可根据需要增加以支持使用。它们不是线程安全的;在没有外部同步时,它们不支持多个线程的并发访问。禁止 null 元素。此类很可能在用作堆栈时快于 Stack,在用作队列时快于 LinkedList。
- ArrayDeque头
public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>, Cloneable, Serializable
上面是ArrayDeque定义信息,我们可以看出,它实现了AbstractCollection。
- 成员变量
transient Object[] elements; //定义一个数组,下面我们可以了解到,他的初始化操作在构造函数中,初始大小是16. transient int head;//头指针,指向队列的头 transient int tail;//尾指针,指向队列的队尾 private static final int MIN_INITIAL_CAPACITY = 8;//数组最小容量。
下面我们看一下,构造函数。
- 构造函数1
public ArrayDeque() { elements = new Object[16];//无参数构函,循环数组的默认大小是16. }
- 构造函数2
public ArrayDeque(int numElements) {//这里用户提供了容量 allocateElements(numElements);//构函调用了这个函数,下面我们 来分析allocateElements(); }
private void allocateElements(int numElements) { //先定义一个变量作为数组的最小容量,也就是上面定义变量:最小容量为8,如果用户输入的小于8,直接就用最小容量,来创建数组。 int initialCapacity = MIN_INITIAL_CAPACITY; //如果大于8,则将initialCapacity 变成与其最接近并大于等于其自身的2的n次方幂 if (numElements >= initialCapacity) { initialCapacity = numElements; initialCapacity |= (initialCapacity >>> 1); initialCapacity |= (initialCapacity >>> 2); initialCapacity |= (initialCapacity >>> 4); initialCapacity |= (initialCapacity >>> 8); initialCapacity |= (initialCapacity >>> 16); initialCapacity++; //如果数组容量太大溢出,直接赋值为2的30次幂 //官方的注释:Too many elements, must back off,Good luck allocating 2 ^ 30 elements if (initialCapacity < 0) initialCapacity >>>= 1; } elements = new Object[initialCapacity]; }
我们看一下:
initialCapacity = numElements;
initialCapacity |= (initialCapacity >>> 1);
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16);
initialCapacity++;
假设我们输入的是17,initialCapacity 被赋值为17,17的二进制为10001
- 10001>>>1=1000,10001|1000=11001;
- 11001>>>2 =110,11001|110=11111;
- 11111>>>4=1,11111|1=11111;
- 11111>>>8=0,11111|0=11111;
11111>>>16=0,11111|0=11111;
最后initialCapacity++,也就是11111+1=100000转化为10进制也就是32。32也确实是大于17最近的2的n次幂。
这个算法的应用是很高效的,时间复杂度是O(1),要比用其他算法求效率要高。
- 构造函数3
public ArrayDeque(Collection<? extends E> c) { allocateElements(c.size());//我们上面已经介绍过了 addAll(c);//把集合元素放在数组中 }
-部分成员方法
接下来,我们来看主要的添加操作:这个类中的操作主要有两个,一个从头加,一个是从尾加。
public void addFirst(E e) { if (e == null) throw new NullPointerException();//健壮性判断 elements[head = (head - 1) & (elements.length - 1)] = e;//每次向头添加都是,头指针自减并与数组长度-1进行与运算。 if (head == tail) doubleCapacity();//如果循环数组加满,我们就扩容。 }
我们来分析 elements[head = (head - 1) & (elements.length - 1)] = e;
由于容量总是2的n次幂,所以总有以下规律
设a是一个整型变量,elements是2的n次幂的整型,那么有
当a=-1时
a&elements-1=elements-1 成立
当0<=a<=elements-1
a&elements-1=a
所以一开始head=0时,就向头中插入元素,实际插入的位置是数组的末尾。
我们在来看下面的doubleCapacity() 扩容方法。
private void doubleCapacity() { assert head == tail; int p = head; int n = elements.length; int r = n - p; // 数组右面的长度 int newCapacity = n << 1;//新容量是旧容量的2倍 if (newCapacity < 0) throw new IllegalStateException("Sorry, deque too big"); Object[] a = new Object[newCapacity]; System.arraycopy(elements, p, a, 0, r);//拷贝数组右边,从head开始,拷贝r个长度,放在新数组从0开始的位置上。 System.arraycopy(elements, 0, a, r, p); //拷贝数组左边,从0开始,拷贝p个长度,放在新数组从r开始的位置上。 elements = a; head = 0; tail = n;//head和tail指针重新初始化 }
我们来看从队尾加的方法
public void addLast(E e) { if (e == null) throw new NullPointerException(); elements[tail] = e;//需要注意的是,在队头插入结点是先走了一位在插入,这里队尾是现在当前位置插入,在“向前”走一位。 if ( (tail = (tail + 1) & (elements.length - 1)) == head)//如果头尾相遇就扩容,这个算法上面已经介绍过了, 和(elements.length - 1)进行与运算是为了尾指针能够自动循环。 doubleCapacity(); }
接下来是删除队头的节点
public E pollFirst() { int h = head; @SuppressWarnings("unchecked") E result = (E) elements[h]; // Element is null if deque empty if (result == null) return null; elements[h] = null; //将要poll的节点位置设置为空 head = (h + 1) & (elements.length - 1);//头指针向后挪一位 return result; }
删除队尾结点
public E pollLast() { int t = (tail - 1) & (elements.length - 1);//因为队尾插入的特点,尾指针现在指向的是尾结点的后一位,所以要tail-1来指向当前的尾结点。 @SuppressWarnings("unchecked") E result = (E) elements[t]; if (result == null) return null; elements[t] = null;//将取出来的尾结点的位置,置空。 tail = t; return result; }
//只取头结点而不删除@SuppressWarnings("unchecked") public E peekFirst() { // elements[head] is null if deque empty return (E) elements[head]; }//只取尾结点的值而不删除 @SuppressWarnings("unchecked") public E peekLast() { return (E) elements[(tail - 1) & (elements.length - 1)]; }
- ArrayDeque循环队列的部分源码分析
- ArrayDeque双端队列源码分析
- 基础数据机构之ArrayDeque队列源码分析
- ArrayDeque源码分析
- ArrayDeque源码分析
- jdk源码分析ArrayDeque
- Java数据结构 -ArrayDeque 双端队列的简单分析
- ArrayDeque的分析
- 深入理解循环队列----循环数组实现ArrayDeque
- ArrayDeque 源码
- ArrayDeque利用数组建造的双向队列
- 循环队列的具体分析
- ArrayDeque 双端队列
- 双向队列ArrayDeque类
- 循环数组 and ArrayDeque
- ThreadPoolExecutor的部分源码分析
- Android ArrayDeque源码
- ArrayDeque源码图析
- Java中的输入和输出重定向
- Twitter的分布式自增ID算法snowflake (Java版)
- 音阶对应频率表
- 关于textField输入光标颜色及cleanButton大小和颜色的设置
- 到底什么是UI设计规范
- ArrayDeque循环队列的部分源码分析
- 7月27日云栖精选夜读:AI时代_运维和测试岗位如何开启"第二春"?
- JS中“创建对象”及“通过原型创建对象”浅析
- 剑指Offer——(43)左旋转字符串
- Regular polygon HDU
- CNN 神经网络介绍
- Lua基础之table详解
- Java Iterator ListIterator 迭代器 以及 并发修改异常 ConcurrentModificationException
- Mac java环境变量配置过程记录