本文深入剖析Java中的优先队列PriorityQueue,从堆的基本概念到源码实现原理,带你全面理解这个重要的数据结构。由于内容较多,分为上下两篇,本篇是上篇,主要讲解基础概念和源码分析。
系列专栏推荐:
- JAVA集合【夜话集】
- JVM知识专栏
- 数据库sql理论与实战【博主踩坑之道】
- 小游戏开发【博主强推 匠心之作 拿来即用无门槛】
想象你正在医院的急诊室。虽然病人按到达顺序排队,但突然来了一位急重症病人,医生会优先处理这位病人。这就是现实生活中的"优先队列":不是先来先服务,而是按照优先级处理。
普通队列就像我们排队买奶茶:
而优先队列则像医院的急诊室:
让我们用代码来直观感受一下区别:
// 普通队列
Queue<String> normalQueue = new LinkedList<>();
normalQueue.offer("小明");
normalQueue.offer("小红");
normalQueue.offer("小张");
// 处理顺序:小明 -> 小红 -> 小张
// 优先队列
PriorityQueue<Patient> emergencyRoom = new PriorityQueue<>((p1, p2) ->
p2.getEmergencyLevel() - p1.getEmergencyLevel());
emergencyRoom.offer(new Patient("小明", 1)); // 普通感冒
emergencyRoom.offer(new Patient("小红", 3)); // 高烧
emergencyRoom.offer(new Patient("小张", 2)); // 扭伤
// 处理顺序:小红(高烧) -> 小张(扭伤) -> 小明(感冒)
优先级可以基于多种规则:
// 数字优先队列(小顶堆)
PriorityQueue<Integer> numbers = new PriorityQueue<>();
numbers.offer(3);
numbers.offer(1);
numbers.offer(2);
System.out.println(numbers.poll()); // 输出1
// 工作任务队列(按优先级排序)
PriorityQueue<Task> taskQueue = new PriorityQueue<>((t1, t2) -> {
// 先比较优先级
if (t1.getPriority() != t2.getPriority()) {
return t2.getPriority() - t1.getPriority();
}
// 优先级相同,比较创建时间
return t1.getCreateTime().compareTo(t2.getCreateTime());
});
优先队列在我们的日常生活和系统开发中随处可见:
class Player {
private String name;
private int ranking;
private long waitTime;
// ... 其他属性
}
PriorityQueue<Player> matchQueue = new PriorityQueue<>((p1, p2) -> {
// 等待时间越长,优先级越高
long timeDiff = p2.getWaitTime() - p1.getWaitTime();
if (timeDiff != 0) {
return (int)timeDiff;
}
// 等待时间相同,按照排位分数匹配
return Math.abs(p1.getRanking() - targetRanking) -
Math.abs(p2.getRanking() - targetRanking);
});
通过这些例子,我们可以看到优先队列的核心特点:
理解了优先队列的本质,我们就能更好地在实际开发中运用它,设计出更合理的系统。接下来,我们将深入探讨它的底层实现原理——堆数据结构。
要理解优先队列,我们必须先理解它的底层数据结构——堆。想象一个公司的组织架构:CEO在最顶端,下面是经理,再下面是普通员工。堆的结构也是类似的层级关系。
堆是一种特殊的完全二叉树,它满足以下特性:
就像公司架构: