栈通常是指先进后出的容器。有时候栈也被称为叠加栈,因为最后压入的元素,第一个弹出栈,有图有真相:
Stack 框架图:
可以看出 Stack 继承了 Vector,实现了 List 接口,也是 Collection 大家族中的一员。下面瞅瞅 Stack 的常规操作:
public static void main(String[] args) {
//创建 Stack 对象
Stack stack = new Stack<>();
String str = "this is a stack";
for (String s : str.split(" ")) {
//将值放入栈中
stack.push(s);
}
System.out.println(stack);
System.out.println("栈的大小:" + stack.size());
System.out.println("栈顶的值:" + stack.peek());
System.out.println("删除并返回栈顶的值:" + stack.pop());
System.out.println("删除栈顶后的栈:" + stack);
//打印结果和上面相反
while (!stack.empty()){
System.out.print(stack.pop() + " ");
}
}
打印结果:
[this, is, a, stack]
栈的大小:4
栈顶的值:stack
删除并返回栈顶的值:stack
删除栈顶后的栈:[this, is, a]
a is this
从代码和打印结果可以看出,Stack 的操作和 LinkedLIst 的操作非常相似:
队列是一个典型的先进先出的容器,即从容器的一端放入事物然后从另一端取出,并且事物放入容器的顺序与取出的顺序是相同的。队列在并发编程中尤为重要,因为它可以安全地将对象从一个任务传输给另一个任务。
Queue 框架图:
通过框架图可以看到 LinkedList 实现了 Queue 接口并且提供了方法以支持队列的行为。简单示例:
public static void main(String[] args) {
//通过 LinkedList 向上转型创建 Queue 对象
Queue queue = new LinkedList<>();
Random random = new Random();
for (int i = 0; i < 10; i++) {
//向队列末尾添加元素
queue.offer(random.nextInt(10));
//queue.add(random.nextInt(10));
}
System.out.println("队列: " + queue);
System.out.println("队列头:" + queue.peek());
System.out.println("队列头:" + queue.element());
System.out.println("删除并返回队列头:" + queue.poll());
System.out.println("删除并返回队列头:" + queue.remove());
}
打印结果:
队列: [2, 6, 4, 5, 3, 5, 9, 6, 2, 6]
队列头:2
队列头:2
删除并返回队列头:2
删除并返回队列头:6
Queue 的使用似曾相识,这些方法在文章 List 简介 中已经介绍过,Queue 接口窄化了对 LinkedList 的方法的访问权限,只对外暴露了以下方法:
先进先出描述了最典型的队列规则,声明的是下一个弹出队列的元素应该是等待时间最长的元素。优先级队列声明下一个弹出元素是最需要的元素,也就是具有最高的优先级,当在 PriorityQueue 中添加一个对象时,这个对象会在队列中被排序,规则是对象在队列中的自然顺序,如果需要修改这个顺序,那么可以通过提供自己的 Comparator 来实现。
/**
* 打印 queue
* @param queue
*/
public static void printQueue(Queue queue){
while(!queue.isEmpty()){
System.out.print(queue.poll() + ",");
}
}
public static void main(String[] args) {
//创建一个整数集合
List list = Arrays.asList(3,6,1,8,5,1,7,9,0,4);
//创建 PriorityQueue 对象
PriorityQueue priorityQueue = new PriorityQueue<>();
priorityQueue.addAll(list);
System.out.println("默认排序:");
//打印
printQueue(priorityQueue);
System.out.println();
//创建倒序的 PriorityQueue 对象
priorityQueue = new PriorityQueue<>(list.size(), new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o2.compareTo(o1);
}
});
priorityQueue.addAll(list);
System.out.println("倒序:");
//打印
printQueue(priorityQueue);
}
打印结果:
默认排序:
0,1,1,3,4,5,6,7,8,9,
倒序:
9,8,7,6,5,4,3,1,1,0,
当使用 PriorityQueue 的无参构造器创建 PriorityQueue 优先级队列的时候,队列中元素的顺序默认是自然排序,如果需要制定排序规则,就需要调用 PriorityQueue 的有参构造器,然后自定义 Comparator 对象重写 compareTo() 方法。而且从打印结果来看,元素的重复是允许的。
中间还有一个小插曲,当使用 PriorityQueue 的 toString() 方法去打印的时候,发现顺序居然不对,最后使用了上述代码中的遍历的方式才打印出理想中的结果。PriorityQueue 的逻辑结构是一棵完全二叉树,存储结构其实就是一个数组,逻辑层次遍历的结果刚好是一个数组。
本章介绍了 Java 的栈和队列的概念和基本使用,补充了优先级队列的相关内容,打印优先级队列的时候需要使用遍历的方式。