ArrayDeque

数组循环队列,这个数据结构设计的挺有意思的。
据说此类很可能在用作堆栈时快于 Stack,在用作队列时快于 LinkedList。

一、容量

1.1默认容量是8=2^3

1.2指定初始化容容量

    public ArrayDeque(int numElements) {
allocateElements(numElements);
}
private void allocateElements(int numElements) {
int initialCapacity = MIN_INITIAL_CAPACITY;
if (numElements >= initialCapacity) {
initialCapacity = numElements;
initialCapacity |= (initialCapacity >>> 1);
initialCapacity |= (initialCapacity >>> 2);
initialCapacity |= (initialCapacity >>> 4);
initialCapacity |= (initialCapacity >>> 8);
initialCapacity |= (initialCapacity >>> 16);
initialCapacity++; if (initialCapacity < 0) // Too many elements, must back off initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements
}
elements = new Object[initialCapacity];
}
此方法是给数组分配初始容量,初始容量并不是numElements,而是大于指定长度的最小的2的幂正数
所以ArrayDeque的容量一定是2的幂整数
计算的方法是用或运算

1.3或运算的特点:

得到的结果大于等于任意一个操作数
结果有趋向每个位都为1的趋势
所以这样运算下来,运算得到的结果的二进制一定是每个位都是1,再加一个,就刚好是2的整数幂了

1.4扩展容量

当头尾指针相遇,则数组存满了
此时要扩展容量,会调用
    private void doubleCapacity() {
assert head == tail;
int p = head;
int n = elements.length;
int r = n - p; // number of elements to the right of p
int newCapacity = n << 1;//bit count faster
if (newCapacity < 0)
throw new IllegalStateException("Sorry, deque too big");
Object[] a = new Object[newCapacity];
System.arraycopy(elements, p, a, 0, r);//copy the right of head(include head)
System.arraycopy(elements, 0, a, r, p);//copy the left of the tail(exclude tail)
elements = a;
head = 0;
tail = n;
}

1.5细说doubleCapacity

注意看:
1.求两本容量,n<<1,使用位运算要快于四则运算,因为这更贴近运算器电路的设计
2.复制原来的数组到目标数组要注意顺序!
可以看到,复制分两次复制进行,第一次复制head指针右边的元素(包括head指针指向的那个),第二次复制left指针左边的元素(不包括tail指针指向的那个)
扩展为原来两本的容量,结果还是2的幂整数
为什么容量一定要是2的幂整数呢?待会说

二、头尾指针

一开始,头尾指针都在下标为0的地方,如果向队头插入数据,头指针向左移,向队尾插入数据,尾指针向右移
tail指针所在的位置,不存储数据,代表下一次addLast存储的地方

三、add

队列提供增加到队首和队尾的两种方法,注意看怎么处理指针临界状态和指针循环

3.1addLast

    public void addLast(E e) {
if (e == null)
throw new NullPointerException();
elements[tail] = e;
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}
移动tail指针,用了与运算
与运算的特点:
结果一定小于等于任意一个操作符的值
与正数进行与运算结果一定为证
当tail+1了之后,超过数组长度,用与运算可以起到循环指针的效果,相当于(tail+1%elements.length)
因为elements.length一定是2的整数幂,当-1了之后每一位一定是1,当tile+1超过数组长度的时候,刚好是2的整数幂,则是10***00这种形式,所以与运算了之后,一定等于0

3.2addFirst

    public void addFirst(E e) {
if (e == null)
throw new NullPointerException();
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
}
当head-1<0的时候,用与运算可以得到绝对值,并且循环指针
因为当head超过数组,head-1刚好是-1,则二进制每个都是1,与运算了之后,一定是111***111的正数,刚好是数组的最后一个位置

四、指针相遇

当tail == head的时候,首尾指针重合,此时队列已满,需要扩展队列,调用doubleCapacity

五、利用空间局部性

在方法中需要多次调用的全局变量,最好创建一个局部变量来访问
因为全局变量是在静态存储区中的,局部变量是放在栈中的,和方法的指令中同一个区域,所以访问会更快,提高程序性能
就像下面这样
    public boolean removeFirstOccurrence(Object o) {
if (o == null)
return false;
int mask = elements.length - 1;
int i = head;
Object x;
while ( (x = elements[i]) != null) {
if (o.equals(x)) {
delete(i);
return true;
}
i = (i + 1) & mask;
}
return false;
}
创建了一个mask,来存放elements.length-1

6.优化删除策略

    private 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) {//如果head在要删除元素的前面
System.arraycopy(elements, h, elements, h + 1, front);//将要删除元素的前继元素往后移动一格
} else { // Wrap around
System.arraycopy(elements, 0, elements, 1, i);//把i前面的元素往后挪一格
elements[0] = elements[mask];
System.arraycopy(elements, h, elements, h + 1, mask - h);//head-数组末端(不包括数组末端) 都往后移一格
}
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;
}
}
这段源码我觉得很值得一看,他用了最优删除策略,都适用与数组实现的数据结构
当删除的时候,先计算删除点是在队列的上半段还是下半段
如果是上半段,则上半段移动一格
这样子可以达到最少的元素移动!
对于这种循环队列,需要注意删除元素的位置,有两种特殊位置

6.1第一种特殊位置

此时删除节点在队列的上半段,但是上半段是断开的
这个时候要移动上半段的话,要分两次移动
第一次移动删除元素前面的,第二次移动头指针到数组末端

6.2第二种特殊位置

查看原文:http://blog.zswlib.com/2016/10/27/jdk%e6%ba%90%e7%a0%81%e5%88%86%e6%9e%90arraydeque/

jdk源码分析ArrayDeque的更多相关文章

  1. JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue

    JDK源码分析—— ArrayBlockingQueue 和 LinkedBlockingQueue 目的:本文通过分析JDK源码来对比ArrayBlockingQueue 和LinkedBlocki ...

  2. JDK 源码分析(4)—— HashMap/LinkedHashMap/Hashtable

    JDK 源码分析(4)-- HashMap/LinkedHashMap/Hashtable HashMap HashMap采用的是哈希算法+链表冲突解决,table的大小永远为2次幂,因为在初始化的时 ...

  3. JDK源码分析(三)—— LinkedList

    参考文档 JDK源码分析(4)之 LinkedList 相关

  4. JDK源码分析(一)—— String

    dir 参考文档 JDK源码分析(1)之 String 相关

  5. JDK源码分析(2)LinkedList

    JDK版本 LinkedList简介 LinkedList 是一个继承于AbstractSequentialList的双向链表.它也可以被当作堆栈.队列或双端队列进行操作. LinkedList 实现 ...

  6. 【JDK】JDK源码分析-LinkedHashMap

    概述 前文「JDK源码分析-HashMap(1)」分析了 HashMap 主要方法的实现原理(其他问题以后分析),本文分析下 LinkedHashMap. 先看一下 LinkedHashMap 的类继 ...

  7. 【JDK】JDK源码分析-HashMap(1)

    概述 HashMap 是 Java 开发中最常用的容器类之一,也是面试的常客.它其实就是前文「数据结构与算法笔记(二)」中「散列表」的实现,处理散列冲突用的是“链表法”,并且在 JDK 1.8 做了优 ...

  8. 【JDK】JDK源码分析-TreeMap(2)

    前文「JDK源码分析-TreeMap(1)」分析了 TreeMap 的一些方法,本文分析其中的增删方法.这也是红黑树插入和删除节点的操作,由于相对复杂,因此单独进行分析. 插入操作 该操作其实就是红黑 ...

  9. 【JDK】JDK源码分析-Vector

    概述 上文「JDK源码分析-ArrayList」主要分析了 ArrayList 的实现原理.本文分析 List 接口的另一个实现类:Vector. Vector 的内部实现与 ArrayList 类似 ...

随机推荐

  1. 打破陈规抓痛点,H3 BPM10.0挑战不可能

    高效益意味着相似的运营活动比竞争对手做得更好,而战略定位则意味着企业在运营活动中有区别于竞争对手的实施方式,即差异化竞争.在新经济体下,面对社会的变革.市场的竞争环境.不断攀升的成本压力,几乎没有企业 ...

  2. OpenGL ES: Array Texture初体验

    [TOC] Array Texture这个东西的意思是,一个纹理对象,可以存储不止一张图片信息,就是说是是一个数组,每个元素都是一张图片.这样免了频繁地去切换当前需要bind的纹理,而且可以节省系统资 ...

  3. JBPM

    JBPM简介 什么是jbpm JBPM,全称是Java Business Process Management(业务流程管理),它是覆盖了业务流程管理.工作流.服务协作等领域的一个开源的.灵活的.易扩 ...

  4. css 填坑常用代码分享

    以下是常用的代码收集,没有任何技术含量,只是填坑的积累.转载请注明出处,谢谢. 因为提交比较麻烦,后来转置github:https://github.com/jsfront/src/blob/mast ...

  5. 【流量劫持】沉默中的狂怒 —— Cookie 大喷发

    精简版:http://www.cnblogs.com/index-html/p/mitm-cookie-crack.html 前言 上一篇文章 讲解了如何借助前端技术,打造一个比 SSLStrip 更 ...

  6. [译]DbContext API中使用SqlQuery和ExecuteSqlCommand获取存储过程的输入输出参数

    水平有限,欢迎指正.原文:http://blogs.msdn.com/b/diego/archive/2012/01/10/how-to-execute-stored-procedures-sqlqu ...

  7. Linux网络编程-tcp缓存设置

    最近发现服务的逻辑完成时间很短,但是上游接收到的时间比较长,所以就怀疑是底层数据的序列化/反序列化.读写.传输有问题,然后怀疑是TCP的读写缓存是不是设置太小.现在就记录下TCP缓存的各配置项以及缓存 ...

  8. Android-环境问题

    大家都知道 Android Studio 是 Goole 的亲儿子,但,亲儿子毕竟也是刚出生2年不到,身上大毛病没有,小毛病大一堆,这篇博文就来总结一下常见的Android Studio 的设置,使用 ...

  9. Python初学者之网络爬虫(二)

    声明:本文内容和涉及到的代码仅限于个人学习,任何人不得作为商业用途.转载请附上此文章地址 本篇文章Python初学者之网络爬虫的继续,最新代码已提交到https://github.com/octans ...

  10. CSS系列目录

    1.  在HTML中引入CSS的方法 2.  CSS选择器 2.1 CSS3新增选择器 3.  CSS的继承与层叠特性 4.  CSS中盒子模型 5.  CSS中盒子之间的关系 6.  CSS中盒子的 ...