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中排序了