Java 集合排序意外行为
集合排序并没有给我预期的结果,还是我误读了方法 要排序的行对象列表:Java 集合排序意外行为,java,sorting,Java,Sorting,集合排序并没有给我预期的结果,还是我误读了方法 要排序的行对象列表: public class Row { private int id; private boolean line; public Row(int id, boolean line) { this.id = id; this.line = line; } public boolean isLine() { return line;
public class Row {
private int id;
private boolean line;
public Row(int id, boolean line) {
this.id = id;
this.line = line;
}
public boolean isLine() {
return line;
}
@Override public String toString() {
return "Row{" + "id=" + id + ", line=" + line + '}';
}
}
起始数据:
[Row{id=0, line=true}, Row{id=1, line=false}, Row{id=2, line=true}, Row{id=3, line=false}]
排序代码:
Collections.sort(rows, new Comparator<Row>(){
@Override public int compare(Row o1, Row o2) {
if (!o1.isLine() && !o2.isLine()) return 0;
if (o1.isLine()) {
return 1;
} else {
return -1;
}
}
});
我得到的印象是,所有具有line=true
的对象都应该位于列表的开头,而不是末尾
如果我稍微更改Compotor实现:
Collections.sort(rows, new Comparator<Row>(){
@Override public int compare(Row o1, Row o2) {
if (!o1.isLine() && !o2.isLine()) return 0;
if (o1.isLine()) {
return -1;
} else {
return 1;
}
}
});
现在可以在列表的开始处找到所有具有line=true
的对象,但它们已切换位置(id=0
应位于第一位)
预期排序结果:
[Row{id=0, line=true}, Row{id=2, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]
我得到的印象是,所有line=true的对象都应该在
列表的开始,而不是结束
与此代码不同:
if (o1.isLine()) {
return 1;
}
表示o1
优于o2
因此,
isLine=true
的对象将出现在末尾,因为默认顺序为升序。可以在列表的开头找到line=true的所有对象 现在,他们已经交换了位置(id=0应该是第一位) 在比较器实现中从不使用
id
。它永远不会被考虑。
要获得: [Row{id=0,line=true},Row{id=2,line=true},Row{id=1,line=false}, 行{id=3,行=false}] 您应该在
行
中添加一个getter来检索id
。
然后您应该首先按照line=true
和id
ASC进行排序。 Collections.sort(rows, new Comparator<Row>(){
@Override public int compare(Row o1, Row o2) {
if (!o1.isLine() && !o2.isLine()) return 0;
if (o1.isLine() && o2.isLine()) {
return o1.getId() > o2.getId();
}
if (o1.isLine()) {
return -1;
} else {
return 1;
}
}
});
我得到的印象是,所有line=true的对象都应该在
列表的开始,而不是结束
与此代码不同:
if (o1.isLine()) {
return 1;
}
表示o1
优于o2
因此,
isLine=true
的对象将出现在末尾,因为默认顺序为升序。可以在列表的开头找到line=true的所有对象 现在,他们已经交换了位置(id=0应该是第一位) 在比较器实现中从不使用
id
。它永远不会被考虑。
要获得: [Row{id=0,line=true},Row{id=2,line=true},Row{id=1,line=false}, 行{id=3,行=false}] 您应该在
行
中添加一个getter来检索id
。
然后您应该首先按照line=true
和id
ASC进行排序。 Collections.sort(rows, new Comparator<Row>(){
@Override public int compare(Row o1, Row o2) {
if (!o1.isLine() && !o2.isLine()) return 0;
if (o1.isLine() && o2.isLine()) {
return o1.getId() > o2.getId();
}
if (o1.isLine()) {
return -1;
} else {
return 1;
}
}
});
你的比较是不对称的,因此被破坏了。使用布尔类中实现的反向比较(请注意lambda正文开头的减号):
你的比较是不对称的,因此被破坏了。使用布尔类中实现的反向比较(请注意lambda正文开头的减号): 总结@Dukeling:
Collections.sort(rows, new Comparator<Row>(){
@Override public int compare(Row o1, Row o2) {
return -Boolean.compare(o1.isLine(), o2.isLine());
}
});
结果:
[Row{id=2, line=true}, Row{id=0, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]
[Row{id=0, line=true}, Row{id=2, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]
总结@Dukeling:
Collections.sort(rows, new Comparator<Row>(){
@Override public int compare(Row o1, Row o2) {
return -Boolean.compare(o1.isLine(), o2.isLine());
}
});
结果:
[Row{id=2, line=true}, Row{id=0, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]
[Row{id=0, line=true}, Row{id=2, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]
您的比较器还应该考虑
id
。如果compare(o1,o2)
返回1,则表示o1更大。第一次调用的结果与此完全一致。您的第一个if语句应该是if(o1.isLine()==o2.isLine())
-否则,如果两者都为真,排序有时会返回1,有时会返回-1(当它应该一直返回0时)。但实际上,您可以用替换整个方法!Boolean.compare(o1.isLine(),o2.isLine())
@CrazySabPath如果您觉得这对未来的访问者有帮助,欢迎您自己在回答中发表我的评论。排序从低到高。您的比较器还应考虑id
。如果compare(o1,o2)
返回1,则表示o1更大。第一次调用的结果与此完全一致。您的第一个if语句应该是if(o1.isLine()==o2.isLine())
-否则,如果两者都为真,排序有时会返回1,有时会返回-1(当它应该一直返回0时)。但实际上,您可以用替换整个方法!Boolean.compare(o1.isLine(),o2.isLine())
@CrazySabPath如果您觉得这对未来的访问者有帮助,欢迎您自己在回答中发表我的评论。排序是从最低到最高的。不,我不依赖这一点,如果语句确保了这一点,请先发表我的评论。为什么这一点会被高估?@Crazysabpath这很奇怪-如果这两行都是假的,那么我们就不关心ID,但如果这两行都是真的,我们会关心吗?如果第一个排序标准相等,那么第二个排序标准应该得到尊重。劳恩,你是对的,我总结了@Dukeling的评论,这非常有效。不,我不相信这一点,第一个if语句确保了这一点。为什么这一点会被高估?@Crazysabpath这很奇怪-如果这两行都是假的,那么我们就不关心ID,但如果这两行都是真的,我们会关心吗?如果第一个排序标准相等,第二个排序标准应该得到尊重。劳恩你是对的,我总结了@Dukeling的评论,效果很好。我认为你指的是Boolean.compare(…)
或Boolean.valueOf(…).compareTo(…)
或Boolean.valueOf(…).compareTo(…)
实现中不考虑id排序。因此,预期的结果是好的,但这只是一个机会。在列表中添加更多对象,id不应按升序排序。@davidxxx尝试了1000个对象,所有id都按升序排序。谢谢您的反馈。我假设在调用sort()
之前,id已经在列表中排序,并且sort()实现在以迭代方式处理时保持顺序。但这并不是定义比较器的可靠方法。算法可能会在运行时或将来的JDK版本中更改。@davidxxx Collections.sort使用稳定的排序算法,这是由Javadoc保证的。@Klitos Kyriacou您有关于它的参考资料吗?您的实现中不考虑id排序。因此,预期的结果是好的,但这只是一个机会。在列表中添加更多对象,id不应按升序排序。@davidxxx尝试了1000个对象,所有id都按升序排序。谢谢您的反馈。我想id已经在t中排序了