ArrayDeque源代码分析
1. 体系结构
了解特性,先看下体系结构:
如上所看到的,知道其支持 序列化,克隆,迭代器操作,队列特性。详细实现 除了实现以上接口外,扩展AbstractCollection 抽象类。
2. 应用场景
大炮打苍蝇。还是鸟枪打野猪?工具应用要有场景:
ArrayDeque 为双端队列,支持首部,尾部两端的操作,因此做双端操作可用于fifo等queue, 做单端操作可做为stack.
然而能做queue的还有linkedList, vector等,能做stack有Stack,还要ArrayDeque干嘛?
还是那句话,“应用”是要讲究“场景”的:
linkedList内部实现用node节点链接前后元素。模拟c/c++的链表(长处在于中间节点的增删操作为o(1))。
vector方法加着synchronized修饰(同步 将带来性能的损耗)。Stack的实现又继承自vector,问题同上。
ArrayDeque 的底层实现为单纯的数组操作。所以单从性能上看。ArrayDeque在优于他们。当然因为没有做同步处理,所以存在并发问题。须要调用方自己保障。
3. 源代码实现
源代码实现部分。看上去还是相对亲切的(当年大学书本上背烂的链表,堆,队列实现),又复习了一遍C语言入门的队列实现。
当然jdk中的设计毕竟是巧妙的。
挑几处分析下:
public boolean isEmpty() {
return head == tail;
}
public int size() {
return (tail - head) & (elements.length - 1);
}
public E pollFirst() {
int h = head;
E result = elements[h]; // Element is null if deque empty
if (result == null)
return null;
elements[h] = null; // Must null out slot
head = (h + 1) & (elements.length - 1);
return result;
}
public E pollLast() {
int t = (tail - 1) & (elements.length - 1);
E result = elements[t];
if (result == null)
return null;
elements[t] = null;
tail = t;
return result;
}
先说明下关键变量 意义:
head 第一个元素的索引
tail 最后一个元素之后的索引。
如 head指向0。tail指向3。那么有效元素为索引0,1,2 指向的元素,所以元素个数 size() 为 3(即3-0)。
看下上述实现奇怪的地方,大量如此的操作,为何?
(tail - head) & (elements.length - 1)
(h + 1) & (elements.length - 1)
原因在于 arraydeque在实现双端队列时才用为循环数组。存在 tail < head的情况,所以要通过 & (elements.length - 1)操作来修正,使其为正值。
所以我们会想到通过补码是实现是能够的,然而单凭 &(elements.length - 1) 就实现修正,还是难以让我们信服的。
如果 tail = 3, head = 6, elements.length=10, 那么结果也应该是 7啊 (元素的索引为6,7。8,9。0,1,2),而非 -3&10 = 8 啊 (-3 补码 1111 1101 10补码 0000 1010 结果 0000 1000 为8)
不思不得其解,再看源代码,当中有一句话
/**
* The minimum capacity that we’ll use for a newly created deque.
* Must be a power of 2.
*/
所以,一切都明确了,elements.length 即数组长度是有要求的,必须是2的幂指数,如此一切可解。測试一下elments.length 为16,32等都顺利成章通过了。原理也非常easy,elments.length 二进制位 00100..00,elments.length-1 为 000111…11能够做掩码了。如此,上述源代码理解通过。
关于arrayDequeue在分配空间,要求必须是 2的幂指数。当中有一段实现:
private void allocateElements(int numElements) {
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);
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 = (E[]) new Object[initialCapacity];
}
关于此处算法的出处。个人眼下还没有理解,哪位大侠知道还请告知。可是能否work,我们还是要验证一下,一下借用一下他人的方法:
public class ArrayDequeCapacity {
public static void main(String[] args) {
for (int i = 1; i < 32; i++) {
int n = (int) Math.pow(2, i) - 1;
System.out.println(i + " " + n + " " + getCapacity(n));
}
}
private static int getCapacity(int numElements) {
int 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
return initialCapacity;
}
}
结果:
1 1 2
2 3 4
3 7 8
4 15 16
5 31 32
6 63 64
7 127 128
8 255 256
9 511 512
10 1023 1024
11 2047 2048
12 4095 4096
13 8191 8192
14 16383 16384
15 32767 32768
16 65535 65536
17 131071 131072
18 262143 262144
19 524287 524288
20 1048575 1048576
21 2097151 2097152
22 4194303 4194304
23 8388607 8388608
24 16777215 16777216
25 33554431 33554432
26 67108863 67108864
27 134217727 134217728
28 268435455 268435456
29 536870911 536870912
30 1073741823 1073741824
31 2147483646 1073741824
从验证结果来看,对于计算大于等于一个数的最小2的幂指数运算,这种方法还是工作的非常好的。
4. 一些问题
那么。问题又来了。为什么分配空间一定要按2的幂指数。我是赞同其它同学观点的(有其它理由的还请补充):
1. 从内存空间分配的角度来讲,2的幂指数。更加快内存分配
linux内存分配管理中的伙伴系统以连续2的幂指数(1。2,4,8,…1024)个页帧(page frame) 为单位进行空间分配,而页帧做为标准内存分配单元大小4K, 所以才用 2的幂指数为大数组分配空间是有利于进行内存管理的。
2. 逻辑推断的简化
如上式:
public int size() {
return (tail - head) & (elements.length - 1);
}
如果没有对 elements.length做要求的话,就要例如以下推断来
int sub = tail - head;
if(sub >= 0)
return sub;
else {
return sub + elements.length;
}
关于arrayDequeue的应用和实现。大致如此。
ArrayDeque源代码分析的更多相关文章
- Android AsyncTask 源代码分析
AsyncTask源代码分析 public abstract class AsyncTask<Params, Progress, Result> { //日志TAG private sta ...
- android-plugmgr源代码分析
android-plugmgr是一个Android插件加载框架,它最大的特点就是对插件不需要进行任何约束.关于这个类库的介绍见作者博客,市面上也有一些插件加载框架,但是感觉没有这个好.在这篇文章中,我 ...
- Twitter Storm源代码分析之ZooKeeper中的目录结构
徐明明博客:Twitter Storm源代码分析之ZooKeeper中的目录结构 我们知道Twitter Storm的所有的状态信息都是保存在Zookeeper里面,nimbus通过在zookeepe ...
- 转:SDL2源代码分析
1:初始化(SDL_Init()) SDL简介 有关SDL的简介在<最简单的视音频播放示例7:SDL2播放RGB/YUV>以及<最简单的视音频播放示例9:SDL2播放PCM>中 ...
- 转:RTMPDump源代码分析
0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://. ...
- 转:ffdshow 源代码分析
ffdshow神奇的功能:视频播放时显示运动矢量和QP FFDShow可以称得上是全能的解码.编码器.最初FFDShow只是mpeg视频解码器,不过现在他能做到的远不止于此.它能够解码的视频格式已经远 ...
- UiAutomator源代码分析之UiAutomatorBridge框架
上一篇文章<UIAutomator源代码分析之启动和执行>我们描写叙述了uitautomator从命令行执行到载入測试用例执行測试的整个流程.过程中我们也描写叙述了UiAutomatorB ...
- MyBatis架构设计及源代码分析系列(一):MyBatis架构
如果不太熟悉MyBatis使用的请先参见MyBatis官方文档,这对理解其架构设计和源码分析有很大好处. 一.概述 MyBatis并不是一个完整的ORM框架,其官方首页是这么介绍自己 The MyBa ...
- hostapd源代码分析(三):管理帧的收发和处理
hostapd源代码分析(三):管理帧的收发和处理 原文链接:http://blog.csdn.net/qq_21949217/article/details/46004379 这篇文章我来讲解一下h ...
随机推荐
- Spearman Rank(斯皮尔曼等级)相关系数
转自:http://blog.csdn.net/wsywl/article/details/5859751 1.简介 在统计学中,斯皮尔曼等级相关系数以Charles Spearman命名,并经常用希 ...
- warning: LF will be replaced by CRLF in dubbo-demo-api/pom.xml.
今天使用git add .的时候出现了一个错误. 错误如下: 解决方案: $ rm -rf .git // 删除.git $ git config --global core.autocrlf fal ...
- emouse思·睿—评论与观点整理之四
虽说我主要做的硬件,平时的兴趣爱好比较关注移动互联网,混迹于虎嗅.爱范儿.雷锋网.36Kr.cnBeta.瘾科技.i黑马.TechWeb等这类科技以及创业媒体,遗憾的是系统的去写的并不多,好在还算充分 ...
- Java学习笔记——File类文件管理及IO读写、复制操作
File类的总结: 1.文件和文件夹的创建 2.文件的读取 3.文件的写入 4.文件的复制(字符流.字节流.处理流) 5.以图片地址下载图片 文件和文件夹 相关函数 (boolean) mkdir( ...
- Android Asynchronous Http Client-Android异步网络请求客户端接口
1.简介 Android中网络请求一般使用Apache HTTP Client或者采用HttpURLConnect,但是直接使用这两个类库需要写大量的代码才能完成网络post和get请求,而使用and ...
- “秘书九段的故事”,要学会给自己制定一个工作N段或者技术N段
总经理要求秘书安排次日上午九点开一个会议.在这件事下,什么是任务?什么是结果? 通知到所有参会的人员,然后秘书自己也参加会议来做服务,这是“任务”.但我们想要的结果是什么呢?下面是一至九段秘书的不同做 ...
- 8 个基于 Lucene 的开源搜索引擎推荐
Lucene是一种功能强大且被广泛使用的搜索引擎,以下列出了8种基于Lucene的搜索引擎,你可以想象它们有多么强大. 1. Apache Solr Solr 是一个高性能,采用Java5开发,基于L ...
- 杨晓峰-Java核心技术-6 动态代理 反射 MD
目录 第6讲 | 动态代理是基于什么原理? 典型回答 考点分析 知识扩展 反射机制及其演进 动态代理 精选留言 Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAnd ...
- 老猪带你玩转自定义控件三——sai大神带我实现ios 8 时间滚轮控件
ios 8 的时间滚轮控件实现了扁平化,带来很好用户体验,android没有现成控件,小弟不才,数学与算法知识不过关,顾十分苦恼,幸好在github上找到sai大神实现代码,甚为欣喜,顾把学习这个控件 ...
- impala 导出CSV 或excel
1.介绍 impala-shell导入导出 参数说明: • -q query (--query=query) 从命令行执行查询,不进入impala-shell • -d default_db (--d ...