从名字我们可以看出,其实一个双向队列实现,而且底层采用数组实现。

public class ArrayDeque<E> extends AbstractCollection<E>
implements Deque<E>, Cloneable, Serializable

从定义可以看出,其实现了 Deque 接口。

原理

为了深入理解 ArrayDeque 的原理,我们将从类成员变量、构造方法、核心方法两个方面逐一介绍。

类成员变量

// 数据数组
transient Object[] elements;
// 头结点
transient int head;
// 尾节点
transient int tail;

从类成员变量我们就可以知道,其底层确实使用数组存储。

构造方法

ArrayDeque 一共有 3 个构造方法:

public ArrayDeque() {
elements = new Object[16];
} public ArrayDeque(int numElements) {
allocateElements(numElements);
} public ArrayDeque(Collection<? extends E> c) {
allocateElements(c.size());
addAll(c);
}

从第一个构造方法可以看到,其构造方法直接指定了 ArrayDeque 的初始大小为 16。

核心方法

对于双向队列来说,其关键的方法是:offer、poll、offerFirst、offerLast、pollFirst、pollLast。但其实这些方法的内容都类似,所以我们只分析 offer 和 poll 方法。

offer

public boolean offer(E e) {
return offerLast(e);
} public boolean offerLast(E e) {
addLast(e);
return true;
} public void addLast(E e) {
if (e == null)
throw new NullPointerException();
elements[tail] = e;
// 当 tail 和 head 相遇时,表示队列已满,需要扩容
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
}

这里比较难懂的地方是这个判断:

if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();

因为 ArrayDeque 初始容量是 16,而每次扩容都是扩为原来的两倍,所以 ArrayDeque 的容量总是 2 的幂次方。所以上面的判断其实在队列未满时,相当于将 tail 进行加一操作。

if ( (tail = (tail + 1)) == head)
doubleCapacity();

而做这样一个与操作的目的就是在 tail 到达数组末尾时可以自动切换为 0。我们可以假设此数组大小为 16,而此时 tail 指向了 15,即末尾节点。那么此时执行 offer 操作,我们在计算 (tail + 1) & (elements.length - 1) 就会如下图所示:

10000   // tail + 1 = 15 + 1 = 16
01111 // element.length - 1 = 16 -1 =15
00000 // 结果为0

计算出的结果为 0,也就是说 tail 指针指向了 0 这个位置。

poll

public E poll() {
return pollFirst();
} 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; // Must null out slot
// 等价于 head = h + 1
// 当处于队列末尾时,会切到队列头
head = (h + 1) & (elements.length - 1);
return result;
}

poll 方法也与 offer 方法类似,在最终对 head 节点加一时也用了同样的方法。

总结

看完 ArrayDeque 类的实现,我们不由得会想起 LinkedList 的实现,因为它们两个都是双向队列的实现,但是一个采用数组实现,一个采用链表实现。那么它们有什么异同呢?

通过查询一些资料发现,其实它们两个在功能和效率上并没有太大不同。如果你需要用到双向嘟列,那么 ArrayDeque 相对于 LinkedList 要更好。因为 ArrayDeque 相对于 LinkedList 直接采用数组存储,而 LinkedList 则需要采用节点存储。所以 LinkedList 相对于 ArrayDeque 需要消耗更多内存。

本身数组与链表的差异在于查询和修改的差异,但是对于队列来说,其都是在头和尾进行操作。所以数组与链表的差异在队列身上没有任何体现。而查阅 ArrayDeque 和 LinkedList 的发布时间,我们会发现 ArrayDeque 发布于 JDK 1.6,而 LinkedList 发布于 JDK 1.2。所以从这一点来看,我们有理由相信 ArrayDeque 其实是 LinkedList 的优化版本。

如果你对 LinkedList 和 ArrayDeque 的差异感兴趣,建议阅读这篇文章:Java: ArrayDeque vs. LinkedList

集合系列 Queue(十一):ArrayDeque的更多相关文章

  1. 集合系列 Queue(九):PriorityQueue

    PriorityQueue 是一个优先级队列,其底层原理采用二叉堆实现.我们先来看看它的类声明: public class PriorityQueue<E> extends Abstrac ...

  2. 集合系列 Queue(十):LinkedList

    我们之前在说到 List 集合的时候已经说过 LinkedList 了.但 LinkedList 不仅仅是一个 List 集合实现,其还是一个双向队列实现. public class LinkedLi ...

  3. 给jdk写注释系列之jdk1.6容器(11)-Queue之ArrayDeque源码解析

    前面讲了Stack是一种先进后出的数据结构:栈,那么对应的Queue是一种先进先出(First In First Out)的数据结构:队列.      对比一下Stack,Queue是一种先进先出的容 ...

  4. 【集合系列】- 深入浅出分析 ArrayDeque

    一.摘要 在 jdk1.5 中,新增了 Queue 接口,代表一种队列集合的实现,咱们继续来聊聊 java 集合体系中的 Queue 接口. Queue 接口是由大名鼎鼎的 Doug Lea 创建,中 ...

  5. 【集合系列】- 初探java集合框架图

    一.集合类简介 Java集合就像一种容器,可以把多个对象(实际上是对象的引用,但习惯上都称对象)"丢进"该容器中.从Java 5 增加了泛型以后,Java集合可以记住容器中对象的数 ...

  6. Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例

    概要  前面,我们已经学习了ArrayList,并了解了fail-fast机制.这一章我们接着学习List的实现类——LinkedList.和学习ArrayList一样,接下来呢,我们先对Linked ...

  7. Java 集合系列14之 Map总结(HashMap, Hashtable, TreeMap, WeakHashMap等使用场景)

    概要 学完了Map的全部内容,我们再回头开开Map的框架图. 本章内容包括:第1部分 Map概括第2部分 HashMap和Hashtable异同第3部分 HashMap和WeakHashMap异同 转 ...

  8. Java集合 之 Queue集合

    什么是Queue集合? 答:Queue用于模拟队列这种数据结构.队列通常是指“先进先出(FIFO)”的容器.队列的头部保存在队列中存放时间最长的元素,尾部保存存放时间最短的元素.新元素插入到队列的尾部 ...

  9. Java 集合系列 13 WeakHashMap

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

随机推荐

  1. C# 异步并发操作,只保留最后一次操作

    在我们业务操作时,难免会有多次操作,我们期望什么结果呢? 绝大部分情况,应该是只需要最后一次操作的结果,其它操作应该无效. 自定义等待的任务类 1. 可等待的任务类 AwaitableTask: // ...

  2. 【Android - IPC】之Serializable和Parcelable序列化

    1.序列化的目的 (1)永久的保存对象数据(将对象数据保存到文件或磁盘中): (2)通过序列化操作将对象数据在网络上进行传输(由于网络传输是以字节流的方式对数据进行传输的,因此序列化的目的是将对象数据 ...

  3. Flask开发VIP版HttpServer #华为云·寻找黑马程序员#

    欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...

  4. 区块链学习笔记:D04 区块链在各行业领域的应用(二)

    这节课主要是政务领域.版权存证领域.能源领域的应用案例介绍 1.房屋租赁联盟链 特点:真实可信.透明补贴.便于追溯.公共监督 节点:房屋运营节点.房管局节点.社保局节点.财政局节点.教育部门节点(多节 ...

  5. Python如何爬取实时变化的WebSocket数据

    一.前言 作为一名爬虫工程师,在工作中常常会遇到爬取实时数据的需求,比如体育赛事实时数据.股市实时数据或币圈实时变化的数据.如下图: Web 领域中,用于实现数据'实时'更新的手段有轮询和 WebSo ...

  6. 基于VMware Workstation下Windows server的搭建

    网络安全学习内容 一.VMware安装Windows系统   1.1安装配置虚拟机 需要提前准备的东西: 配置网络实验室的IP: 为了满足实验中一些需要用到网络的需求,学校为我们提供了每个人学号密码的 ...

  7. 使用 font-spider 对 webfont 网页字体进行压缩

    原文链接:使用 font-spider 对 webfont 网页字体进行压缩 随着当前 Web 技术的日新月异,网页界面内容越来越丰富,让人眼花缭乱,其中就包括了网页中的各种自定义字体. 例如,个人博 ...

  8. luogu P1168 中位数 |树状数组+二分

    题目描述 给出一个长度为NN的非负整数序列A_i,对于所有1 ≤ k ≤ (N + 1) / 21≤k≤(N+1)/2,输出A_1, A_3, -, A_2k - 1的中位数.即前1,3,5,-个数的 ...

  9. HDU3032 Nim or not Nim?(Lasker’s Nim游戏)

    Nim or not Nim? Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

  10. cs5642-v3各配置文件测试图片对比

    寄存器功能强大,可以配置很多参数,每个参数出来的图像都不同 我们以cs5642-v3的两个配置进行了对比测试 测试准备 配置文件:ov5642_960p_RAW和OV5642_YUV_VGA 测试摄像 ...