什麼是隊列
隊列是一種先進入的元素先出的線性表結構。這個特性正好和棧的特性相反。
我們知道,棧只支持兩個基本操作:入棧 push()和出棧 pop()。隊列跟棧非常相似,支持的操作也很有限,最基本的操作也是兩個:入隊 enqueue(),放一個數據到隊列尾部;出隊 dequeue(),從隊列頭部取一個元素。
手動實現隊列
隊列的實現方式也有兩種,一種是通過數組實現,叫順序隊列,另外一種是通過鏈表實現,叫鏈式隊列。
順序隊列
public static class MyQueue<E> {
private Object[] array;
private int head;
private int tail;
public MyQueue(int capacity) {
this.array = new Object[capacity];
}
public boolean enqueue(E e) {
array[tail] = e;
int length = array.length;
tail = (++tail) % length;
if (tail == head) {
// 擴容
Object[] data = new Object[length * 2];
int index = 0;
int i = head;
do {
data[index++] = array[i];
i = (++i) % length;
} while (i != head);
tail = length;
head = 0;
array = data;
}
return true;
}
public E dequeue() {
if (head == tail) {
return null;
}
E item = (E) array[head];
array[head] = null;
head = (++head) % (array.length);
return item;
}
}
鏈式隊列
public static class MyQueue<E> {
private Node head;
private Node tail;
public MyQueue() {
}
public MyQueue(E data) {
head = tail = new Node(data);
}
public boolean enqueue(E data) {
if (head == null) {
this.head = new Node(data);
this.tail = head;
} else {
Node node = new Node(data);
tail.next = node;
tail = node;
}
return true;
}
public E dequeue() {
if (head == null) {
return null;
} else {
E item = (E) head.data;
head = head.next;
return item;
}
}
}
public static class Node<E> {
private E data;
private Node next;
public Node(E data) {
this.data = data;
this.next = null;
}
public Node(E data, Node next) {
this.data = data;
this.next = next;
}
}
JDK 中的隊列實現
如果不想自己實現隊列,那麼我們可以使用 JDK 提供的隊列實現。
主要實現有:
- ArrayDeque
- LinkedList
下面看下使用LinkedList
來實現Queue的例子:
public static class MyQueue<E> {
private LinkedList<E> queue;
public MyQueue() {
this.queue = new LinkedList<E>();
}
public MyQueue(E e) {
this.queue = new LinkedList<E>();
queue.add(e);
}
public boolean enqueue(E e) {
queue.offer(e);
return true;
}
public E dequeue() {
if (queue.isEmpty()) {
return null;
} else {
return queue.pop();
}
}
}
常見的隊列種類
循環隊列、阻塞隊列、併發隊列。它們在很多偏底層系統、框架、中間件的開發中,起着關鍵性的作用。比如高性能隊列 Disruptor、Linux 環形緩存,都用到了循環併發隊列;Java concurrent 併發包利用 ArrayBlockingQueue 來實現公平鎖等。
循環隊列
在上面的順序隊列的實現過程中,我們使用了循環隊列的實現方式。使用循環隊列,可以避免在數組中對數據進行搬移。
阻塞隊列
所謂阻塞隊列是指:在隊列是空的情況下從隊列頭部取一個元素,操作會被阻塞直到隊列中被插入一個元素,在隊列滿的情況下,向隊列中插入一個元素,操作也會被阻塞,直到其他操作從隊列中取出一個元素爲止。
我們發現,通過阻塞隊列可以很好的實現一個生產者和消費者模式。
- ArrayBlockingQueue.java
- BlockingDeque.java
- DelayQueue.java
- LinkedBlockingDeque.java
- LinkedBlockingQueue.java
- LinkedTransferQueue.java
- PriorityBlockingQueue.java
- ScheduledThreadPoolExecutor
- SynchronousQueue.java
- TransferQueue.java
Java 中提供了以上的阻塞隊列相關的實現或者接口,其中的隊列實現也是線程安全的。
併發隊列
線程安全的隊列實現,參考Java併發包下面的隊列實現。
隊列的應用場景
隊列可以應用在任何有限資源池中,用於排隊請求,比如數據庫連接池等。實際上,對於大部分資源有限的場景,當沒有空閒資源時,基本上都可以通過“隊列”這種數據結構來實現請求排隊。