java - 集合排序意外行为

标签 java sorting

集合排序没有给我预期的结果,还是我误读了方法?

要排序的 Row 对象列表:

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;
            }
        }
    });

结果:

[Row{id=1, line=false}, Row{id=3, line=false}, Row{id=0, line=true}, Row{id=2, line=true}]

我的印象是所有带有 line=true 的对象都应该在列表的开头,而不是结尾。

如果我稍微改变 Comporator 的实现:

    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;
            }
        }
    });

结果:

[Row{id=2, line=true}, Row{id=0, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]

所有具有 line=true 的对象现在都可以在列表的开头找到,但它们已经交换了位置(id=0 应该是第一个)。

预期排序结果:

[Row{id=0, line=true}, Row{id=2, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]

最佳答案

I was under the impresion that all objects with line=true should be at the start of the list, not the end.

不像这段代码:

  if (o1.isLine()) {
       return 1;
  } 

表示o1优于o2
因此带有 isLine=true 的对象将发生在最后,因为默认顺序是升序。

All of objects with line=true can be found at the start of the list now, but they have switched places (id=0 should be first).

您永远不会在比较器实现中使用 id
绝对不能考虑。

得到:

[Row{id=0, line=true}, Row{id=2, line=true}, Row{id=1, line=false}, Row{id=3, line=false}]

您应该在 Row 中添加一个 getter 来检索 id。 那么您应该首先按 line=trueid 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;
        }
    }
 });

一种更简洁的编写方式是使用 Java 8 Comparator:

Comparator<Row> comparatorRow = Comparator.comparing(Row::isLine).reversed()
                                          .thenComparing(Row::getId);

由于行已经按 id 排序并且排序保证是稳定的:相同的元素不会因为排序而重新排序,您可以只比较在 isLine 上:

Comparator<Row> comparatorRow = Comparator.comparing(Row::isLine).reversed();

关于java - 集合排序意外行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48110333/

相关文章:

java - 如何在同一行上system.out.println(java)

javascript - 在保留键的同时按值的属性对 Map 进行排序

java - 将 OpenTelemetry 跟踪数据从 Spring Boot 应用程序导入 Elastic APM - View 缺少数据

java - 如何用java代码给系统用户添加密码

java - 在 android/java 中访问 PouchDB?

java - 哪个 TaskExecutor 用于异步日志记录?

sorting - Redis:在 alpha 中排序哈希 "fields"

swift - 将混合的 String-Int 字符串按数字顺序排序为主要顺序,然后按字母顺序快速排序

c - 在 C 中使用快速排序反向排序(降序)?

java - 对数组列表进行排序