前言

  因为自己在刷LeetCode的过程中经常用到ArrayDeque,所以在这里简单介绍一下ArrayDeque以及总结一下ArrayDeque的常用方法。(写的有问题的地方还望大佬指正,感激不尽)

  Java 6 中引入了Deque接口,ArrayDeque实现了这个接口。Array是数组,Deque是双端队列(可以理解为两头都可以插入删除的队列),ArrayDeque就是实现为循环数组的一个队列。详情请看官方api文档 链接   先来看看官方的介绍

Java中array的 java arraydeque_后缀


翻译一下大致可以知道以下几点:

  1. ArrayDeque由一个可变的数组实现,这个数组没有大小限制;
  2. ArrayDeque不是线程安全的;
  3. 当ArrayDeque被当作栈使用时比Stack快,当作队列使用时比LinkedList快。
  4. 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系列的效果

Java中array的 java arraydeque_java_02

  deque为空时使用poll系列的效果

Java中array的 java arraydeque_常用方法_03

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,防止弄混嘛。