一、摘要
在 jdk1.5 中,新增了 Queue 接口,代表一种队列集合的实现,咱们继续来聊聊 java 集合体系中的 Queue 接口。
Queue 接口是由大名鼎鼎的 Doug Lea 创建,中文名为道格·利,关于这位大神,会在后期进行介绍,翻开 JDK1.8 源代码,可以将 Queue 接口旗下的实现类抽象成如下结构图:
Queue 接口,主要实现类有:ArrayDeque、LinkedList、PriorityQueue。
关于 LinkedList 实现类,在之前的文章中已经有所介绍,今天咱们来介绍一下 ArrayDeque 这个类,如果有理解不当之处,欢迎指正。
二、简介
在介绍 ArrayDeque 类之前,可以从上图中看出,ArrayDeque 实现了 Deque 接口,Deque 是啥呢,全称含义为double ended queue
,即双端队列。Deque 接口的实现类可以被当作 FIFO(队列)使用,也可以当作 LIFO(栈)来使用。
其中队列(FIFO)表示先进先出,比如水管,先进去的水先出来;栈(LIFO)表示先进后出,比如,手枪弹夹,最后进去的子弹,最先出来。
ArrayDeque 是 Deque 接口的一种具体实现,所以,既可以当成队列,也可以当成栈来使用,类定义如下:
public class ArrayDeque<E> extends AbstractCollection<E>
implements Deque<E>, Cloneable, Serializable{
}
当作为队列使用时,我们会将它与 LinkedList 类来做对比,在后文,我们会做测试类来将两者进行详细数据对比。因为 Deque 接口继承自 Queue接口,在这里,我们分别列出两者接口所定义的方法,两者内容区别如下:
当作为栈使用时,难免会将它与 Java 中一个叫做 Stack 的类做比较,Stack 类的数据结构也是后进先出,可以作为栈来使用,我们分别列出 Stack 类和 Deque 接口所定义的方法,两者内容区别如下:
虽然,ArrayDeque 和 Stack 类都可以作为栈来使用,但是 ArrayDeque 的效率要高于 Stack 类,并且功能也比 Stack 类丰富的多,当需要使用栈时,Java 已不推荐使用 Stack,而是推荐使用更高效的 ArrayDeque,次选 LinkedList 。
从上面两张图中可以看出,Deque 总共定义了 2 组方法,添加、删除、取值都有两套方法,它们功能相同,区别是对失败情况的处理不同,一组方法是遇到失败会抛异常,另一组方法是遇到失败会返回null
。
方法虽然定义的很多,但无非就是对容器的两端进行添加、删除、查询操作,明白这一点,那么使用起来就很简单了。
继续回到咱们要介绍的这个 ArrayDeque 类,从名字上可以看出 ArrayDeque 底层是通过数组实现的,为了满足可以同时在数组两端插入或删除元素的需求,该数组还必须是循环的,即循环数组,也就是说数组的任何一点都可能被看作起点或者终点。
因为是循环数组,所以 head 不一定总是指向下标为 0 的数组元素,tail 也不一定总是比 head 大。
这一点,我们可以通过 ArrayDeque 源码分析得出这些结论,打开 ArrayDeque 的源码分析,可以看到,主要有3个关键参数:
- elements:用于存放数组元素。
- head:用于指向数组中头部下标。
- tail:用于指向数组中尾部下标。
public class ArrayDeque<E> extends AbstractCollection<E>
implements Deque<E>, Cloneable, Serializable{
/**用于存放数组元素*/
transient Object[] elements;
/**用于指向数组中头部下标*/
transient int head;
/**用于指向数组中尾部下标*/
transient int tail;
/**最小容量,必须为2的幂次方*/
private static final int MIN_INITIAL_CAPACITY = 8;
}
与此同时,ArrayDeque 提供了三个构造方法,分别是默认容量,指定容量及依据给定的集合中的元素进行创建,其中默认容量为 16。
public ArrayDeque() {
//默认初始化数组大小为 16
elements = new Object[16];
}
指定容量初始化方法,源码如下:
public ArrayDeque(int numElements) {
//指定容量
allocateElements(numElements);
}
我们来看看指定容量调用的allocateElements
方法,源码如下:
private void allocateElements(int numElements) {
elements = new Object[calculateSize(numElements)];
}
calculateSize
方法&#