文章目录
- 1.Queue 接口
- 2.LinkedList
- 3.ArrayDeque
- 4.PriorityQueue
- 5.总结
队列是一种特殊的线性数据结构,在数据的尾部插入元素,在数据的头部删除元素。通常以 FIFO(先进先出)的方式存储和访问数据。Java 中提供了
Queue
接口来实现队列,常用的实现类有
LinkedList
、
ArrayDeque
以及
PriorityQueue
。
1.Queue 接口
Queue
提供的是除了基本的 Collection
操作外的其他插入、抽取和检查方法。每个方法都存在两种形式:一种是在失败时抛出一个异常,一种是返回一个特殊值,如 null
或 false
。 队列的实现通常不允许插入 null
元素,但某些实现(如 ArrayLIst
)并不禁止插入 null
。即使在允许传入 null
的实现中,也不应该将 null
放入到其中,因为 null
通常用作表示队列不包含元素的特殊值。
下面是对应的 API:
方法 | 抛出异常操作 | 返回特殊值操作 |
---|---|---|
插入 | add(e) | offer(e) |
移除 | remove() | poll() |
检查 | element() | peek() |
插入操作对应源码:
public interface Queue<E> extends Collection<E> {
/**
* 如果可以在不违反容量限制的情况下立即将指定的元素插入到此队列中,成功时返回true,
* 如果当前没有可用空间,则抛出IllegalStateException
*
* @param e 需要插入的元素
* @return 如果插入成功,则返回true,否则返回false
* @throws IllegalStateException 如果当前没有空间
* @throws ClassCastException 如果指定元素的类不允许它添加到此队列
* @throws NullPointerException 如果指定元素为null,且此队列不允许null元素
* @throws IllegalArgumentException 如果指定元素的某些属性不允许它添加到此队列
*/
boolean add(E e);
/**
* 如果可以在不违反容量限制的情况下立即将指定的元素插入到此队列中,成功时返回true,否则返回false。
*
* @param e 需要插入的元素
* @return 如果插入成功,则返回true,否则返回false
* @throws ClassCastException 如果指定元素的类不允许它添加到此队列
* @throws NullPointerException 如果指定元素为null,且此队列不允许null元素
* @throws IllegalArgumentException 如果指定元素的某些属性不允许它添加到此队列
*/
boolean offer(E e);
}
移除操作对应源码:
public interface Queue<E> extends Collection<E> {
/**
* 检索并删除此队列的头部。此方法与poll()方法的不同之处在于,如果此队列为空,则抛出异常。
*
* @return 此队列的头部
* @throws NoSuchElementException 如果此队列为空
*/
E remove();
/**
* 检索并删除此队列的头部。此方法与remove()方法的不同之处在于,如果此队列为空,则返回null
*
* @return 此队列的头部,如果此队列为空,则返回null
*/
E poll();
}
检查操作对应源码:
public interface Queue<E> extends Collection<E> {
/**
* 检测但不删除此队列的头部。此方法与peek()方法的不同之处在于,如果此队列为空,则抛出异常。
*
* @return 此队列的头部
* @throws NoSuchElementException 如果此队列为空
*/
E element();
/**
* 检测但不删除此队列的头部。此方法与element()方法的不同之处在于,如果此队列为空,则返回null
*
* @return 此队列的头部,如果此队列为空,则返回null
*/
E peek();
}
2.LinkedList
LinkedList
实现了 Queue
接口,并提供了协作式的多线程访问机制。LinkedList
可以作为栈、队列、双向队列的实现,提供了大量的方法来满足各种使用场景。
对应类图下:
Queue<String> queue = new LinkedList<>();
可以使用 LinkedList
类的 add()
方法或 offer()
方法往队列中添加元素,使用 remove()
方法或 poll()
方法来从队列中删除元素,使用 element()
方法或 peek()
方法来查看队列头部的元素。
// 添加元素
queue.add("one");
queue.add("two");
queue.add("three");
// 查看队首元素
System.out.println(queue.element()); // 输出:one
// 移除队首元素
System.out.println(queue.remove()); // 输出:one
// 查看队首元素
System.out.println(queue.peek()); // 输出:two
由于 LinkedList
还可以作为双向队列使用,所以除了提供和 Queue
接口中相同的方法外,LinkedList
还提供了一些双向队列特有的方法,如添加、获取第一个元素和最后一个元素:
// 创建双端队列
LinkedList<String> deque = new LinkedList<>();
// 在队首插入元素
deque.addFirst("one");
// 在队尾插入元素
deque.addLast("two");
// 在队尾插入元素
deque.addLast("three");
// 查看队首元素
System.out.println(deque.getFirst()); // 输出:one
// 查看队尾元素
System.out.println(deque.getLast()); // 输出:three
// 移除队首元素
System.out.println(deque.removeFirst()); // 输出:one
// 移除队尾元素
System.out.println(deque.removeLast()); // 输出:three
3.ArrayDeque
ArrayDeque
是另一种 Queue
接口的实现,内部使用循环数组进行实现。ArrayDeque
是线程不安全的,如果需要在多线程中并发访问队列,需要使用 Collections.synchronizedDeque()
方法将其转化为线程安全的。
对应类图下:
Queue<String> queue = new ArrayDeque<>();
// 添加元素
queue.offer("one");
queue.offer("two");
queue.offer("three");
// 查看队首元素
System.out.println(queue.element()); // 输出:one
// 移除队首元素
System.out.println(queue.remove()); // 输出:one
// 查看队首元素
System.out.println(queue.peek()); // 输出:two
和 LinkedList
一样,ArrayDeque
也可以作为栈和双向队列使用。
public static void main(String[] args) {
// 用作栈(后进先出)
ArrayDeque<String> stack = new ArrayDeque<>();
// 添加元素(压栈)
stack.push("one");
stack.push("two");
stack.push("three");
// 查看栈顶元素
System.out.println(stack.peek()); // 输出:three
// 移除栈顶元素
System.out.println(stack.pop()); // 输出:three
// 用作双向队列(先进先出)
ArrayDeque<String> queue = new ArrayDeque<>();
// 添加元素
queue.addFirst("one");
queue.addLast("two");
queue.addLast("three");
// 查看队首元素
System.out.println(queue.getFirst()); // 输出:one
// 查看队尾元素
System.out.println(queue.getLast()); // 输出:three
}
4.PriorityQueue
PriorityQueue
是一种基于优先级的队列,队列头部元素始终是队列中优先级最高的元素。Java 的 PriorityQueue
是通过二叉小顶堆实现的。在 PriorityQueue
中添加元素后,队列会自动维护这些元素的优先级,优先级最高的元素会被放置在队列头部。
对应类图下:
public static void main(String[] args) {
Queue<Integer> queue = new PriorityQueue<>();
// 添加元素
queue.offer(5);
queue.offer(2);
queue.offer(9);
// 查看队首元素
System.out.println(queue.element()); // 输出:2
// 移除队首元素
System.out.println(queue.remove()); // 输出:2
// 查看队首元素
System.out.println(queue.peek()); // 输出:5
}
PriorityQueue
也可以自定义元素的比较器,在构造 PriorityQueue
对象的时候传入相应的比较器对象:
public static void main(String[] args) {
// 创建基于字符串长度比较的优先队列
PriorityQueue<String> queue = new PriorityQueue<>(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// 按照字符串长度比较
return o1.length() - o2.length();
}
});
// 添加元素
queue.offer("Java");
queue.offer("Python");
queue.offer("C");
// 查看队首元素
System.out.println(queue.element()); // 输出:C
// 移除队首元素
System.out.println(queue.remove()); // 输出:C
// 查看队首元素
System.out.println(queue.peek()); // 输出:Java
}
5.总结
下面我们将对上面介绍的三个类进行简要的比较和总结:
特性 / 类型 | LinkedList | ArrayDeque | PriorityQueue |
---|---|---|---|
底层数据结构 | 双向链表 | 动态数组(环形) | 堆(Heap) |
顺序 | FIFO(First-In-First-Out) | FIFO(First-In-First-Out) | 按照自然排序或比较器排序 |
允许null元素 | 是 | 否 | 否 |
多线程安全 | 否 | 否 | 否 |
主要功能 | 队列操作、栈操作、列表操作 | 队列操作、栈操作 | 优先级队列操作 |
- LinkedList:它是一个基于双向链表的实现,每个元素都包含前向和后向的链接,所以它的插入和删除操作通常都很快。它实现了
List
接口和Deque
接口,因此它不仅可以用作队列,还可以用作栈或双端队列。LinkedList
允许插入null
元素。 - ArrayDeque:它是一个基于动态数组实现的队列,它采用了环形数组的方式来优化空间使用。
ArrayDeque
比LinkedList
在队列和栈操作上更高效,因为它没有链表节点的额外开销。但是,它不支持null
元素。 - PriorityQueue:这是一种特殊类型的队列,它的元素按照自然排序或者比较器定义的顺序排序。也就是说,当你从
PriorityQueue
中检索元素时,总是获取优先级最高(或者最低,取决于比较器)的元素。这使得PriorityQueue
成为实现优先级队列的理想选择。