Java队列 PriorityQueue

本文介绍了Java中的优先队列PriorityQueue,它不同于普通的FIFO队列,而是根据元素的优先级决定出队顺序。PriorityQueue默认按元素的自然顺序排序,也可通过Comparator自定义排序规则。文中通过实例展示了如何创建并使用PriorityQueue,以及如何修复Comparator中可能导致A10优先级高于A2的问题。

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/120829314
本文出自【赵彦军的博客】

Java队列 Queue
Java队列 Deque
Java队列 PriorityQueue
Java栈 Stack
Java阻塞队列 LinkedBlockingDeque

文章目录

PriorityQueue

我们知道,Queue是一个先进先出(FIFO)的队列。

在银行柜台办业务时,我们假设只有一个柜台在办理业务,但是办理业务的人很多,怎么办?

可以每个人先取一个号,例如:A1A2A3……然后,按照号码顺序依次办理,实际上这就是一个Queue

如果这时来了一个VIP客户,他的号码是V1,虽然当前排队的是A10A11A12……但是柜台下一个呼叫的客户号码却是V1

这个时候,我们发现,要实现“VIP插队”的业务,用Queue就不行了,因为Queue会严格按FIFO的原则取出队首元素。我们需要的是优先队列:PriorityQueue

PriorityQueueQueue的区别在于,它的出队顺序与元素的优先级有关,对PriorityQueue调用remove()poll()方法,返回的总是优先级最高的元素。

要使用PriorityQueue,我们就必须给每个元素定义“优先级”。我们以实际代码为例,先看看PriorityQueue的行为:

public class Main {
    public static void main(String[] args) {
        Queue<String> q = new PriorityQueue<>();
        // 添加3个元素到队列:
        q.offer("apple");
        q.offer("pear");
        q.offer("banana");
        System.out.println(q.poll()); // apple
        System.out.println(q.poll()); // banana
        System.out.println(q.poll()); // pear
        System.out.println(q.poll()); // null,因为队列为空
    }
}

我们放入的顺序是"apple""pear""banana",但是取出的顺序却是"apple""banana""pear",这是因为从字符串的排序看,"apple"排在最前面,"pear"排在最后面。

因此,放入PriorityQueue的元素,必须实现Comparable接口,PriorityQueue会根据元素的排序顺序决定出队的优先级。

如果我们要放入的元素并没有实现Comparable接口怎么办?PriorityQueue允许我们提供一个Comparator对象来判断两个元素的顺序。我们以银行排队业务为例,实现一个PriorityQueue

public class Main {
    public static void main(String[] args) {
        Queue<User> q = new PriorityQueue<>(new UserComparator());
        // 添加3个元素到队列:
        q.offer(new User("Bob", "A1"));
        q.offer(new User("Alice", "A2"));
        q.offer(new User("Boss", "V1"));
        System.out.println(q.poll()); // Boss/V1
        System.out.println(q.poll()); // Bob/A1
        System.out.println(q.poll()); // Alice/A2
        System.out.println(q.poll()); // null,因为队列为空
    }
}

class UserComparator implements Comparator<User> {
    public int compare(User u1, User u2) {
        if (u1.number.charAt(0) == u2.number.charAt(0)) {
            // 如果两人的号都是A开头或者都是V开头,比较号的大小:
            return u1.number.compareTo(u2.number);
        }
        if (u1.number.charAt(0) == 'V') {
            // u1的号码是V开头,优先级高:
            return -1;
        } else {
            return 1;
        }
    }
}

class User {
    public final String name;
    public final String number;

    public User(String name, String number) {
        this.name = name;
        this.number = number;
    }

    public String toString() {
        return name + "/" + number;
    }
}

实现PriorityQueue的关键在于提供的UserComparator对象,它负责比较两个元素的大小(较小的在前)。UserComparator总是把V开头的号码优先返回,只有在开头相同的时候,才比较号码大小。

上面的UserComparator的比较逻辑其实还是有问题的,它会把A10排在A2的前面,请尝试修复该错误。

小结

PriorityQueue实现了一个优先队列:从队首获取元素时,总是获取优先级最高的元素。

PriorityQueue默认按元素比较的顺序排序(必须实现Comparable接口),也可以通过Comparator自定义排序算法(元素就不必实现Comparable接口)。

### JavaPriorityQueue 的使用方法 #### 什么是 PriorityQueue? `PriorityQueue` 是 Java 集合框架的一部分,位于 `java.util` 包中。它是一个基于堆结构的无界队列,默认情况下实现最小堆(min-heap),即每次取出的是当前队列中优先级最低的对象。 #### 构造函数 以下是常见的几种构造方式: 1. **默认构造函数** 创建一个按照自然顺序排列的优先队列。 ```java PriorityQueue<Integer> pq = new PriorityQueue<>(); ``` 2. **指定初始容量** 可以为队列分配初始容量。 ```java PriorityQueue<Integer> pq = new PriorityQueue<>(initialCapacity); ``` 3. **自定义比较器** 如果对象不支持自然排序或者需要特定的排序逻辑,可以通过传入 `Comparator` 来定制排序规则[^1]。 ```java PriorityQueue<Integer> minHeap = new PriorityQueue<>((a, b) -> a - b); PriorityQueue<String> customOrder = new PriorityQueue<>((s1, s2) -> s2.length() - s1.length()); ``` #### 常见方法及其说明 | 方法名 | 描述 | |---------------------|--------------------------------------------------------------------------------------| | `add(E e)` | 将指定元素插入此优先队列。如果成功则返回 true;否则抛出异常。 | | `offer(E e)` | 功能同 `add()`,但在失败时不会抛出异常而是返回 false。 | | `remove(Object o)` | 删除队列中的某个对象实例。如果找到并删除,则返回 true;否则返回 false。 | | `poll()` | 获取并移除队列头部的元素(具有最高优先级的元素)。如果没有元素存在,则返回 null。 | | `peek()` | 返回但不移除队列头部的元素。如果没有元素存在,则返回 null。 | | `size()` | 返回队列中的元素数量。 | | `comparator()` | 返回用于对此队列进行排序的 Comparator 实例。如果使用自然顺序创建,则返回 null[^2]。 | #### 示例代码 下面展示如何使用 `PriorityQueue` 并结合自定义比较器: ```java import java.util.PriorityQueue; import java.util.Comparator; public class Main { public static void main(String[] args) { // 默认按升序排列 (最小堆) PriorityQueue<Integer> minHeap = new PriorityQueue<>(); minHeap.add(5); minHeap.add(10); minHeap.add(1); System.out.println("Min Heap Poll: " + minHeap.poll()); // 输出 1 // 自定义最大堆 PriorityQueue<Integer> maxHeap = new PriorityQueue<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2.compareTo(o1); // 改变方向形成最大堆 } }); maxHeap.offer(5); maxHeap.offer(10); maxHeap.offer(1); System.out.println("Max Heap Peek: " + maxHeap.peek()); // 输出 10 } } ``` 上述例子展示了两种不同类型的堆:一种是最小堆,另一种通过自定义比较器实现了最大堆。 --- #### 注意事项 1. **线程安全性** `PriorityQueue` 不是线程安全的数据结构。如果多个线程同时访问同一个队列,需手动同步操作。 2. **null 和不可比较对象** 队列不允许存储 `null` 元素,并且所有元素都应可相互比较。如果不满足这些条件,在运行时会引发错误。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值