Java的PriorityQueue是不稳定还是不确定或两者兼而有之?

时间:2018-02-14 21:24:22

标签: java priority-queue deterministic

我正在浏览Java PriorityQueue的文档并遇到一个重要的注意事项:

  

如果多个元素绑定的值最小,则头部是其中之一   这些因素 - 关系被打破任意

我想了解那个粗体字的实际含义。我感兴趣的两个属性是PriorityQueue的稳定性及其确定性。

据我所知,一个稳定的算法将(来自维基百科):

  

保留输入集的原始顺序

确定性算法(来自维基百科):

  

给定特定输入,[...]产生相同的输出

以下是我的例子:

public class PriorityQueueTest {
    public static class Herp implements Comparable<Herp>{
        int attribute;
        String name;

        Herp(int attribute,String name){
            this.attribute=attribute;
            this.name=name;
        }
        @Override 
        public String toString(){
            return name+": "+attribute;
        }
        @Override
        public int compareTo(Herp o) {
            return Integer.compare(o.attribute, this.attribute);
        }

    }
    public static void main(String[] args){
            PriorityQueue<Herp> queue= new PriorityQueue<Herp>();
            queue.add(new Herp(1,"Fred"));
            queue.add(new Herp(1,"James"));
            queue.add(new Herp(2,"Bob"));
            queue.add(new Herp(3,"Rachel"));
            queue.add(new Herp(4,"Constance"));
            List<Herp> debug= new ArrayList<>();
            while(!queue.isEmpty()){
                debug.add(queue.poll());
            }
            System.out.println(Arrays.toString(debug.toArray()));
    }
}

对我来说,此代码输出以下内容:

[Constance: 4, Rachel: 3, Bob: 2, Fred: 1, James: 1]

如果我切换Fred和James的插入顺序,我会得到相同的结果;因此PriorityQueue不稳定。但是,我无法否定其决定论。无论我尝试什么插入顺序,无论我运行多少次代码,Fred总是会出现在James之前。

但是,我担心这种行为是计算机或JVM特有的(在我看来会使它变得不确定),或者存在一些计数器示例,其中输出会有所不同。

另一种可能性是,虽然目前所有JVM和计算机上的实现可能都是确定性的,但此属性未指定,并且可能在将来某个时候发生变化。

我可以理解,如果内部实现基于某种ObjectID或内部内存值打破了平局,这将使其不稳定但确定性。然而,它也可以根据系统时间,随机数,月相等进行。这将使它既不稳定又不确定。

TLDR:Java的PriorityQueue确定性是什么?

This question,尽管它的标题,证明了算法的不稳定性,它的答案只是链接到不能回答其决定论的文档。

4 个答案:

答案 0 :(得分:2)

虽然Javadoc留下了解释空间,但Open-JDK implementation却没有:

根据sourceoffer(E e)在队列末尾添加元素(必要时增加其大小),然后调用{JAVadoc说明以下内容的private void siftUp(int k, E x)

  

在位置k处插入项目x,通过提升维护堆不变量   x在树上直到它大于或等于其父级,或者是   根。

因此,实现完全是确定性的,并且取决于比较器和插入顺序。

但是,由于这似乎是一个实现细节,并且您的代码依赖于特定的元素顺序,您真的不应该依赖它,并让您的Comparator始终返回不同的值以避免打破平局。

答案 1 :(得分:1)

它不稳定:如果关系被任意打破就不可能。这意味着实现可以自由选择它想要首先返回的绑定元素。稳定的实现必须按照它们插入队列的顺序返回绑定的元素。

它不一定是确定性的或非确定性的:它可以选择如何任意打破平局,所以它可以以确定的方式做到这一点(即如果你放入相同的元素,你可以在相同的顺序,无论顺序是什么)或非确定性的方式(即相同的元素不以相同的顺序出现)。

答案 2 :(得分:0)

来自Java Doc:

  

优先级队列的元素按照它们的顺序排序   自然排序,或由队列建设时提供的比较器   时间

所以这取决于您如何定义比较器中的排序,或compareTo Herp方法(如果它实现Comparable)。

想要确定性吗?编写一个使用辅助属性打破关系的比较器。也许是Herp对象名称的文本。

答案 3 :(得分:0)

我认为应该有一些错误。 PriorityQueue不使用X射线来排列项目。因此,如果项目相同,则最终订单应取决于广告订单。这是我的结果:

[Constance: 4, Rachel: 3, Bob: 2, Fred: 1, James: 1]
[Constance: 4, Rachel: 3, Bob: 2, James: 1, Fred: 1]

实际上,订单是确定性的。但它与插入顺序不同。队列修改的历史也很重要。但那里没有任何随机发生器。