我如何让一个PriorityQueue对我想要它排序的东西进行排序?

另外,在提供和添加方法之间有区别吗?


当前回答

Java 8解决方案

我们可以使用Java 8中引入的lambda表达式或方法引用。如果我们有一些String值存储在优先队列中(容量为5),我们可以提供内联比较器(基于String的长度):

使用lambda表达式

PriorityQueue<String> pq=
                    new PriorityQueue<String>(5,(a,b) -> a.length() - b.length());

使用方法参考

PriorityQueue<String> pq=
                new PriorityQueue<String>(5, Comparator.comparing(String::length));

然后我们可以使用它们中的任何一个:

public static void main(String[] args) {
        PriorityQueue<String> pq=
                new PriorityQueue<String>(5, (a,b) -> a.length() - b.length());
       // or pq = new PriorityQueue<String>(5, Comparator.comparing(String::length));
        pq.add("Apple");
        pq.add("PineApple");
        pq.add("Custard Apple");
        while (pq.size() != 0)
        {
            System.out.println(pq.remove());
        }
    }

这将打印:

Apple
PineApple
Custard Apple

要反转顺序(将其更改为max-priority queue),只需在inline comparator中更改顺序或使用reversed as:

PriorityQueue<String> pq = new PriorityQueue<String>(5, 
                             Comparator.comparing(String::length).reversed());

我们也可以使用Collections.reverseOrder:

PriorityQueue<Integer> pqInt = new PriorityQueue<>(10, Collections.reverseOrder());
PriorityQueue<String> pq = new PriorityQueue<String>(5, 
                Collections.reverseOrder(Comparator.comparing(String::length))

我们可以看到集合。reverseOrder重载以获取比较器,这对自定义对象很有用。反转函数实际上使用Collections.reverseOrder:

default Comparator<T> reversed() {
    return Collections.reverseOrder(this);
}

Offer () vs add()

根据文件

offer方法尽可能插入一个元素,否则返回 假的。这与合集不同。方法,该方法可能失败 仅通过抛出未经检查的异常来添加元素。报价 方法是为失败是正常使用而设计的,而不是 异常发生,例如在固定容量(或“有界”)中 队列。

在使用有容量限制的队列时,offer()通常比add()更可取,后者只能通过抛出异常而无法插入元素。PriorityQueue是一个基于优先级堆的无界优先级队列。

其他回答

使用构造函数重载,它接受Comparator<?super E>比较器,并传入一个比较器,它会按照排序顺序进行比较。如果你给出一个你想要如何排序的例子,如果你不确定的话,我们可以提供一些示例代码来实现比较器。(其实很简单。)

正如在其他地方所说:offer和add只是不同的接口方法实现。在JDK源代码中,添加calls offer。虽然add和offer通常具有潜在的不同行为,因为offer能够表示由于大小限制而不能添加值,但这种差异在PriorityQueue中是无关的,因为PriorityQueue是无界的。

下面是一个优先级队列按字符串长度排序的例子:

// Test.java
import java.util.Comparator;
import java.util.PriorityQueue;

public class Test {
    public static void main(String[] args) {
        Comparator<String> comparator = new StringLengthComparator();
        PriorityQueue<String> queue = new PriorityQueue<String>(10, comparator);
        queue.add("short");
        queue.add("very long indeed");
        queue.add("medium");
        while (queue.size() != 0) {
            System.out.println(queue.remove());
        }
    }
}

// StringLengthComparator.java
import java.util.Comparator;

public class StringLengthComparator implements Comparator<String> {
    @Override
    public int compare(String x, String y) {
        // Assume neither string is null. Real code should
        // probably be more robust
        // You could also just return x.length() - y.length(),
        // which would be more efficient.
        if (x.length() < y.length()) {
            return -1;
        }
        if (x.length() > y.length()) {
            return 1;
        }
        return 0;
    }
}

输出如下:

短 媒介 确实很长

没有区别,正如在javadoc中声明的那样:

public boolean add(E e) {
    return offer(e);
}

作为使用Comparator的替代方法,您还可以让您在PriorityQueue中使用的类实现Comparable(并相应地重写compareTo方法)。

注意,如果排序是对象的直观排序,那么通常最好只使用Comparable而不是Comparator——例如,如果您有一个用例要按年龄对Person对象排序,那么可能最好只使用Comparator。

import java.lang.Comparable;
import java.util.PriorityQueue;

class Test
{
    public static void main(String[] args)
    {
        PriorityQueue<MyClass> queue = new PriorityQueue<MyClass>();
        queue.add(new MyClass(2, "short"));
        queue.add(new MyClass(2, "very long indeed"));
        queue.add(new MyClass(1, "medium"));
        queue.add(new MyClass(1, "very long indeed"));
        queue.add(new MyClass(2, "medium"));
        queue.add(new MyClass(1, "short"));
        while (queue.size() != 0)
            System.out.println(queue.remove());
    }
}
class MyClass implements Comparable<MyClass>
{
    int sortFirst;
    String sortByLength;

    public MyClass(int sortFirst, String sortByLength)
    {
        this.sortFirst = sortFirst;
        this.sortByLength = sortByLength;
    }

    @Override
    public int compareTo(MyClass other)
    {
        if (sortFirst != other.sortFirst)
            return Integer.compare(sortFirst, other.sortFirst);
        else
            return Integer.compare(sortByLength.length(), other.sortByLength.length());
    }

    public String toString()
    {
        return sortFirst + ", " + sortByLength;
    }
}

输出:

1, short
1, medium
1, very long indeed
2, short
2, medium
2, very long indeed

只需将适当的Comparator传递给构造函数:

PriorityQueue(int initialCapacity, Comparator<? super E> comparator)

offer和add之间的唯一区别是它们所属的接口。offer属于Queue<E>接口,而add最初出现在Collection<E>接口。除此之外,这两种方法做的事情完全相同——将指定的元素插入优先级队列。

我还想知道打印订单的问题。举个例子:

对于优先级队列:

PriorityQueue<String> pq3 = new PriorityQueue<String>();

这段代码:

pq3.offer("a");
pq3.offer("A");

可能打印不同于:

String[] sa = {"a", "A"}; 
for(String s : sa)   
   pq3.offer(s);

我从另一个论坛的讨论中找到了答案,其中一个用户说,“offer()/add()方法只将元素插入队列。如果你想要一个可预测的顺序,你应该使用peek/poll来返回队列的头。”