天天看點

java隊列deque,集合之隊列[Queue, Deque],雙端隊列[ArrayDeque, LinkedList],優先級隊列[PriorityQueue]...

資料結構中的隊列了解以下,"先進先出"是隊列的最大的特點,也就是隻能在頭部通路一個元素,在尾部添加一個元素。還有一種叫做雙端隊列。可以有效地在頭部和尾部同時添加或删除元 素。 不支援在隊列中間添加元素。

在 JDK6 中引人了 Deque 接口, 并由 ArrayDeque 和 LinkedList 類實作。這兩個類都提供了雙端隊列, 而且在必要時可以增加隊列的長度。在并發包下還提供了有限隊列和有限雙端隊列。

隊列接口Queue

首先看看在JDK中,隊列接口中都定義了哪些方法:

public interface Queue extends Collection {

// 繼承自Collection接口的方法,

// 在容量允許的情況下将元素放入隊列傳回true,空間不夠時抛出異常

boolean add(E e);

// 在容量允許的情況下插入元素,在容量限制時,優于add方法,空間不夠導緻添加失敗傳回false,

boolean offer(E e);

// 傳回并且删除隊列頭的元素,隊列為空抛出異常

E remove();

// 傳回并且删除隊列頭的元素,隊列為空傳回null

E poll();

傳回但不删除隊列頭的元素,隊列為空抛出異常

E element();

傳回但不删除隊列頭的元素,隊列為空傳回null

E peek();

}

雙端隊列接口Deque

public interface Deque extends Queue {

void addFirst(E e);

void addLast(E e);

boolean offerFirst(E e);

boolean offerLast(E e);

E removeFirst();

E removeLast();

E pollFirst();

E pollLast();

E getFirst();

E getLast();

E peekFirst();

E peekLast();

void push(E e);

E pop();

}

是不是很簡單,方法都是見名知意,而且還是成雙成對的出現,需要注意的系列差別就是為空時通路元素是抛出異常還是傳回null。

還有一些方法繼承自Queue接口和Collection接口:

java隊列deque,集合之隊列[Queue, Deque],雙端隊列[ArrayDeque, LinkedList],優先級隊列[PriorityQueue]...

數組實作的雙端隊列ArrayDeque

仍然先看ArrayDeque的繼承層次:

java隊列deque,集合之隊列[Queue, Deque],雙端隊列[ArrayDeque, LinkedList],優先級隊列[PriorityQueue]...

先看看ArrayDeque中定義的屬性和構造函數:

public class ArrayDeque extends AbstractCollection

implements Deque, Cloneable, Serializable {

transient Object[] elements;

transient int head;

transient int tail;

private static final int MIN_INITIAL_CAPACITY = 8;

// 預設構造16容量的數組建構隊列

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,最小也要保證8個容量,當我們使用指定數量的容量時,需要進行計算才能得到。那麼是怎麼計算的呢,需要遵循什麼原則:

// 根據别的集合建構隊列

public ArrayDeque(Collection extends E> c) {

allocateElements(c.size());

addAll(c);

}

// numElements 指定的容量大小

public ArrayDeque(int numElements) {

allocateElements(numElements);

}

// 根據使用者傳入的計算允許的容量,并建立數組

private void allocateElements(int numElements) {

elements = new Object[calculateSize(numElements)];

}

// 為了計算得到比numElements大的最小2的n次幂

private static int calculateSize(int numElements) {

int initialCapacity = MIN_INITIAL_CAPACITY; // MIN_INITIAL_CAPACITY = 8

// Find the best power of two to hold elements.

// Tests "<=" because arrays aren't kept full.

if (numElements >= initialCapacity) {

initialCapacity = numElements;

// 系列的移位、或操作,隻是為了得到大于numElements的最小2次幂

initialCapacity |= (initialCapacity >>> 1);

initialCapacity |= (initialCapacity >>> 2);

initialCapacity |= (initialCapacity >>> 4);

initialCapacity |= (initialCapacity >>> 8);

initialCapacity |= (initialCapacity >>> 16);

initialCapacity++;

// 如果進過移為操作後,得到的結果第32位為1,1代表負數,那麼就需要回退一位。

if (initialCapacity < 0) // Too many elements, must back off

initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements

}

return initialCapacity;

}

還有一點就是添加元素的過程中,如果超出了數組的限制,那麼怎麼做的,其實和ArrayList一樣,動态的增加數組的容量:

private void doubleCapacity() {

assert head == tail;

int p = head;

int n = elements.length;

int r = n - p; // number of elements to the right of p

int newCapacity = n << 1;

if (newCapacity < 0)

throw new IllegalStateException("Sorry, deque too big");

Object[] a = new Object[newCapacity];

System.arraycopy(elements, p, a, 0, r);

System.arraycopy(elements, 0, a, r, p);

elements = a;

head = 0;

tail = n;

}

将數組增加位原來的2倍,然後拷貝資料到新數組中

知道了ArrayDeque是基于循環數組的實作,那麼對于接口方法的實作就不列舉了,具體可以檢視API文檔。

連結清單實作的雙端隊列 LinkedList

優先隊列

優先隊列按照任意的順序将元素插入到隊列中,但是出隊的時候卻按照從小到大的順序取出,它使用的是一種高效的資料結構——堆(Heap),是一種自我調整的二叉樹。元素之間需要比較,是以插入的元素需要實作Comparable接口,當然,也可以在構造器中提供一個Compator。

它的繼承層次比較簡單,如下:

java隊列deque,集合之隊列[Queue, Deque],雙端隊列[ArrayDeque, LinkedList],優先級隊列[PriorityQueue]...

需要注意的是PriorityQueue并沒有實作Cloneable接口,也就是說它不能被克隆

PriorityQueue每次通路的總是隊列中最小的元素。

public class PriorityQueue extends AbstractQueue

implements java.io.Serializable {

private static final int DEFAULT_INITIAL_CAPACITY = 11;

transient Object[] queue;

private final Comparator super E> comparator;

}

基于數組實作,預設容量為11。當容量不夠的時候,如何增加數組的長度?

private void grow(int minCapacity) {

int oldCapacity = queue.length;

// Double size if small; else grow by 50%

int newCapacity = oldCapacity + ((oldCapacity < 64) ?

(oldCapacity + 2) :

(oldCapacity >> 1));

// overflow-conscious code

if (newCapacity - MAX_ARRAY_SIZE > 0)

newCapacity = hugeCapacity(minCapacity);

queue = Arrays.copyOf(queue, newCapacity);

}

private static int hugeCapacity(int minCapacity) {

if (minCapacity < 0) // overflow

throw new OutOfMemoryError();

return (minCapacity > MAX_ARRAY_SIZE) ?

Integer.MAX_VALUE :

MAX_ARRAY_SIZE;

}

至此,JDK中常用的集合類就完畢了。