Deque用法及原理讲解
Posted 叶长风
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Deque用法及原理讲解相关的知识,希望对你有一定的参考价值。
Deque用法及原理讲解
最近想着对现有知识点进行一个总结,决定从集合开始,想想便从Deque开始吧,Deque用的比较少,但是还是一个功能十分强大的队列,这种双向队列即可以支持先进后出,也能支持先进先出的格式,相当于同时实现了Stack和Vector,今天就来讲一讲Deque用法以及底层源码。
1. Deque用法
先写一个简单的demo,这个demo也是以前查Deque时看别人写的,然后对着写了一遍,如下:
package cn.com.queue.deque;
import java.util.ArrayDeque;
import java.util.Deque;
/**
* @author xiaxuan
*/
public class DequeTest
public static void main(String[] args)
Deque<Integer> mDeque = new ArrayDeque<>();
for (int i = 0; i < 5; i++)
mDeque.offer(i);
System.out.println(mDeque.peek());
System.out.println("********集合方式遍历*********");
//集合方式遍历,元素不会被移除
for (Integer x : mDeque)
System.out.println(x);
System.out.println("********遍历队列***********");
//队列方式遍历,元素逐个被移除
while (mDeque.peek() != null)
System.out.println(mDeque.poll());
System.out.println("**********进栈操作*********");
mDeque.push(10);
mDeque.push(15);
mDeque.push(24);
print(mDeque);
System.out.println("*********出栈操作***********");
System.out.println(mDeque.pop());
public static void print(Deque<Integer> queue)
//集合方式遍历,元素不会被移除
for (Integer x : queue)
System.out.println(x);
运行结果如下图:
上图中
2. Deque原理讲解
首先我们看Deque的实现类ArrayQueue的数据结构,可以看到ArrayQueue还是使用数组的结构,应该来说数组是实现集合类的基础数据结构。
/**
* The array in which the elements of the deque are stored.
* The capacity of the deque is the length of this array, which is
* always a power of two. The array is never allowed to become
* full, except transiently within an addX method where it is
* resized (see doubleCapacity) immediately upon becoming full,
* thus avoiding head and tail wrapping around to equal each
* other. We also guarantee that all array cells not holding
* deque elements are always null.
*/
transient Object[] elements; // non-private to simplify nested class access
现在看ArrayQueue的offer操作,源码如下:
/**
* Inserts the specified element at the end of this deque.
*
* <p>This method is equivalent to @link #offerLast.
*
* @param e the element to add
* @return @code true (as specified by @link Queue#offer)
* @throws NullPointerException if the specified element is null
*/
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;
if ( (tail = (tail + 1) & (elements.length - 1)) == head)
doubleCapacity();
这里的offer操作实际上就是把元素添加到数组的头部, 空间不够了则再对空间进行扩容,扩容的操作就不讲了,实际上就是数组的copy。
然后我们再来看一下push操作,push是栈特有的方法,此处如果是先进先出的操作,那么这里应该就是讲元素添加到数组的第一个位置,然后后面的元素逐个后移,我们来看看push源码的实现。
// *** Stack methods ***
/**
* Pushes an element onto the stack represented by this deque. In other
* words, inserts the element at the front of this deque.
*
* <p>This method is equivalent to @link #addFirst.
*
* @param e the element to push
* @throws NullPointerException if the specified element is null
*/
public void push(E e)
addFirst(e);
// The main insertion and extraction methods are addFirst,
// addLast, pollFirst, pollLast. The other methods are defined in
// terms of these.
/**
* Inserts the specified element at the front of this deque.
*
* @param e the element to add
* @throws NullPointerException if the specified element is null
*/
public void addFirst(E e)
if (e == null)
throw new NullPointerException();
elements[head = (head - 1) & (elements.length - 1)] = e;
if (head == tail)
doubleCapacity();
这里倒是有点意思,说明前面的思考确实是不对的,这里用的是head、tail两个指针,tail用来往头部添加元素,head用来往数组尾部添加元素,如果head == tail则进行扩容。
那再看看数组的peak操作。
/**
* Retrieves, but does not remove, the head of the queue represented by
* this deque, or returns @code null if this deque is empty.
*
* <p>This method is equivalent to @link #peekFirst.
*
* @return the head of the queue represented by this deque, or
* @code null if this deque is empty
*/
public E peek()
return peekFirst();
public E peekFirst()
// elements[head] is null if deque empty
return (E) elements[head];
peak就是弹出队列的头部元素,就是head指针指向的元素,这个比较比较容易理解。
现在再看下poll操作,poll每次操作元素时,会逐个移除队列头部元素。
/**
* Retrieves and removes the head of the queue represented by this deque
* (in other words, the first element of this deque), or returns
* @code null if this deque is empty.
*
* <p>This method is equivalent to @link #pollFirst.
*
* @return the head of the queue represented by this deque, or
* @code null if this deque is empty
*/
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) & (elements.length - 1);
return result;
这里就是将头部的元素取出并返回,然后将头部的元素置为null,然后head值加一。
再来看看栈的pop操作,想必和poll类似。
/**
* Pops an element from the stack represented by this deque. In other
* words, removes and returns the first element of this deque.
*
* <p>This method is equivalent to @link #removeFirst().
*
* @return the element at the front of this deque (which is the top
* of the stack represented by this deque)
* @throws NoSuchElementException @inheritDoc
*/
public E pop()
return removeFirst();
/**
* @throws NoSuchElementException @inheritDoc
*/
public E removeFirst()
E x = pollFirst();
if (x == null)
throw new NoSuchElementException();
return x;
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) & (elements.length - 1);
return result;
pop的操作和poll方法实现是相同的,其实这个可以理解,因为queue和stack各自分别是先进先出与先进后出的模式,所以取数据都是一样的。
3. 综上
双端队列作为Queue和Stack的双重实现,但是在使用的时候只能选择一种使用,不能Queue与Stack的api同时使用。
以上是关于Deque用法及原理讲解的主要内容,如果未能解决你的问题,请参考以下文章