前言
因为自己在刷LeetCode的过程中经常用到ArrayDeque,所以在这里简单介绍一下ArrayDeque以及总结一下ArrayDeque的常用方法。(写的有问题的地方还望大佬指正,感激不尽)
Java 6 中引入了Deque接口,ArrayDeque实现了这个接口。Array是数组,Deque是双端队列(可以理解为两头都可以插入删除的队列),ArrayDeque就是实现为循环数组的一个队列。详情请看官方api文档 链接 先来看看官方的介绍
翻译一下大致可以知道以下几点:
- ArrayDeque由一个可变的数组实现,这个数组没有大小限制;
- ArrayDeque不是线程安全的;
- 当ArrayDeque被当作栈使用时比Stack快,当作队列使用时比LinkedList快。
- ArrayDeque属于Java Collection中的一员,它以及它的迭代器实现了所有Collection和Iterator接口的所有可选方法;
基本属性
ArrayDeque有三个基本属性。首先就是存储元素的数组,其次就是首尾指针。看看IDEA中的源码:
/**
* The array in which the elements of the deque are stored.
* All array cells not holding deque elements are always null.
* The array always has at least one null slot (at tail).
* 翻译一下:
* 这是存储deque中的元素的数组
* 所有不包含deque数组元素的单元总是为null(所以ArrayDeque不允许添加null)
* 这个数组总是至少有一个空的单元(在tail指针处)用来插入元素的
*/
transient Object[] elements;
/**
* The index of the element at the head of the deque (which is the
* element that would be removed by remove() or pop()); or an
* arbitrary number 0 <= head < elements.length equal to tail if
* the deque is empty.
* 翻译一下:
* head是在deque头部的元素的索引,0<=head<elements.length,当队列是null时head=tail
* 数据结构中介绍过,队列的出队都是从队首出的,入队都是从队尾进入的。
*/
transient int head;
/**
* The index at which the next element would be added to the tail
* of the deque (via addLast(E), add(E), or push(E));
* elements[tail] is always null.
* 翻译一下:
* 和head类似,这是队尾指针,添加元素都是从队尾添加的
* elements[tail]永远是null,因为它要留一个位置用来添加。
*/
transient int tail;
构造
ArrayDeque的构造提供了三种方法,无参的、初始化容量的以及用集合做参数的。
1、无参构造函数 (刷题基本都是这样定义的)
/**
* Constructs an empty array deque with an initial capacity
* sufficient to hold 16 elements.
* 构造一个有初始化容量的空array deque,能容纳16个元素,+1应该是为了留一个给tail来做入队操作。
*/
public ArrayDeque() {
elements = new Object[16 + 1];
}
eg:
Deque<Integer> deque=new ArrayDeque<>();
//注意这样定义只是先定义了一个长度为17(因为element[tail]恒为null,最后留一个给tail指针)的数组,不代表deque的大小是16,没插入元素之前它的size还是为0。
deque.size() //仍然是 0
2、有初始容量的构造方法:
/**
* Constructs an empty array deque with an initial capacity
* sufficient to hold the specified number of elements.
* @param numElements lower bound on initial capacity of the deque
* 构造一个有初始容量的空array deque来容纳特定数量的elements
* numElements是是初始容量的下界
* 说人话就是初始化一个numElements大小的object数组。
* 如果numElements<1(是的numElements<1也不会报错)就 new Object[1]
* 如果numElements==Integer.MAX_VALUE 就 new Object[Integer.MAX_VALUE]
* 否则就是 new Object[numElements+1],+1应该还是留给tail做入队
*/
public ArrayDeque(int numElements) {
elements =
new Object[(numElements < 1) ? 1 :
(numElements == Integer.MAX_VALUE) ? Integer.MAX_VALUE :
numElements + 1];
}
eg:
Deque<Integer> deque=new ArrayDeque<>(4);
//注意和无参的一样,"4"只代表new Object[5]
deque.size() //还是 0
3、用集合做参数:
/**
* Constructs a deque containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator. (The first element returned by the collection's
* iterator becomes the first element, or <i>front</i> of the
* deque.)
*构造一个容纳集合中元素的deque,顺序由集合的迭代器iterator决定,iterator返回的第一个元素会成为deque的第一个元素
* @param c the collection whose elements are to be placed into the deque
* 参数c是元素要放入deque的集合
* @throws NullPointerException if the specified collection is null
* 如果集合为空会抛出NnllPointerException的异常
*/
public ArrayDeque(Collection<? extends E> c) {
this(c.size()); //用集合的大小为初始容量
copyElements(c); //将集合中的元素拷贝到数组中
}
常用方法
前文已经说过,ArrayDeque由可变数组实现,容量方面我们不用操心,刷题过程中使用最多的就是出队、入队的操作。下面重点介绍这些操作:
ArrayDeque常用方法:
public void addFirst(E e) //
public void addLast(E e) //
public boolean offerFirst(E e) //
public boolean offerLast(E e) //
/**
* 普通的队列只允许在队尾添加元素,在队首删除元素
* ArrayDeque之所以被称为双端队列就是因为在队尾队首插入删除元素都可以
* 这四个函数都是添加元素的函数,有"Last"后缀就是添加在队尾,"First"后缀就是添加在队首
* 这四个函数只有返回值类型的区别,但更推荐使用offer系列,毕竟有返回值更好判断添加成不成功
* 当e==null时,这四个函数都会抛出NullPointerException空指针的异常
*
* 官方在Deque接口的api文档中说:当deque当前没有可用空间时(就是满时)使用add系列会抛出IllegalStateException的异常
* 但是ArrayDeque的文档中以及源码里面,使用add系列不会抛出IllegalStateException异常,
* 仅仅只是当e==null时会抛出NullPointerException空指针的异常,
* 毕竟ArrayDeque会自动扩容的,应该不会满
*/
public E removeFirst()
public E removeLast()
public E pollFirst()
public E pollLast()
/**
*与添加一样,有"First"后缀的就是在队首删除、有"Last"后缀的就是在队尾删除,并返回删除的元素
*当deque为空时,remove系列会抛出NoSuchElementException的异常
* poll系列会返回null
* 推荐使用poll系列,不仅仅是因为更新一些,谁愿意刷题一提交出现异常的错误呢
*/
deque为空时使用remove系列的效果
deque为空时使用poll系列的效果
public E getFirst()
public E getLast()
public E peekFirst()
public E peekLast()
/**
* 这四个函数是取队首队尾元素的函数,有"First"后缀的就是在队首取、有"Last"后缀的就是在队尾取
* 使用poll系列、remove系列也可以取元素,只不过取了就删除了,而get系列、peek系列只取,不删除
* 当deque为空时,使用get系列也会抛出NoSuchElementException的异常
* peek系列只会返回null
* 推荐使用peek系列
*/
ArrayDeque实现的Collection中的常用方法:
//返回deque中的元素个数,前文已经使用过
public int size()
//判断deque是否为空
public boolean isEmpty()
Deque<Integer> deque=new ArrayDeque<>(4);
deque.isEmpty() //此时为true,因为deque中还没有任何元素,有的只是一个Object[5]
//判断deque中是否包含对象o,是则返回true,否则返回false
public boolean contains(Object o)
ArrayDeque实现的Qeque中的常用方法:
public boolean add(E e)
public boolean offer(E e)
/**
* 这两个函数分别等价于addLast()、offerLast(),在队尾添加元素
* e为null时二者都会抛出NullPointerException的异常
*/
public E remove()
public E poll()
/**
* 这两个函数分别等价于removeFirst()、poll(),在队首删除元素并返回出队的元素
* 当队列为空时使用remove会抛出NoSuchElementException的异常
* 使用poll会返回null
*/
public E element()
public E peek()
/**
* 这两个函数分别等价于getFirst()、peekFirst(),返回队首的元素,不会删除队首的元素
* * 当队列为空时使用element会抛出NoSuchElementException的异常
* 使用peek会返回null
*/
ArrayDeque实现的Stack中的常用方法:
public void push(E e)
public E pop()
/**
* 分别等价于addFirst()、removeFirst(),毕竟栈的出栈入栈都是在栈顶嘛,队首相当于栈顶
* 这两个在ArrayDeque中一般用不到
*/
总结
在ArrayDeque中,如果把它当普通的队列用,推荐使用offer(),poll(),peek();如果把它当双端队列用,推荐使用offerFirst()、pollFirst()、peekFirst()等一系列函数;如果要使用栈,还是建议使用Stack,防止弄混嘛。