线性表结构:队列
什么是队列
队列是一种先进入的元素先出的线性表结构。这个特性正好和栈的特性相反。
我们知道,栈只支持两个基本操作:入栈 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并发包下面的队列实现。
队列的应用场景
队列可以应用在任何有限资源池中,用于排队请求,比如数据库连接池等。实际上,对于大部分资源有限的场景,当没有空闲资源时,基本上都可以通过“队列”这种数据结构来实现请求排队。
人生的主旋律其实是苦难,快乐才是稀缺资源。在困难中寻找快乐,才显得珍贵~