我正在浏览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,尽管它的标题,证明了算法的不稳定性,它的答案只是链接到不能回答其决定论的文档。
答案 0 :(得分:2)
虽然Javadoc留下了解释空间,但Open-JDK implementation却没有:
根据source,offer(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]
实际上,订单是确定性的。但它与插入顺序不同。队列修改的历史也很重要。但那里没有任何随机发生器。