【问题标题】:Replace CompareToBuilder with Java 8's Comparator.comparing(...).thenComparing(...)将 CompareToBuilder 替换为 Java 8 的 Comparator.comparing(...).thenComparing(...)
【发布时间】:2016-11-05 16:08:40
【问题描述】:

在 Java 8 之前,我们这样实现 Comparable.compareTo(...)

public int compare(Person a, Person b) {
    return new CompareToBuilder()
            .append(a.getLastName(), b.getLastName())
            .append(a.getFirstName(), b.getFirstName())
            .toComparison();
}

从 Java 8 开始,我们可以这样做:

public int compare(Person a, Person b) {
    return Comparator
            .comparing(Person::getLastName)
            .thenComparing(Person::getFirstName)
            .compare(a, b);
}

新的 Java 8 方式可能允许我们删除 commons-lang3 依赖项。 新的 Java 8 速度更快吗?有没有办法自动迁移?我没有找到 IntelliJ 的意图。


请注意,当存在逆序且涉及非自然比较时,它会变得有点复杂:

public int compare(SingleBenchmarkResult a, SingleBenchmarkResult b) {
    return new CompareToBuilder()
            .append(b.hasAnyFailure(), a.hasAnyFailure()) // Reverse
            .append(a.getAverageScore(), b.getAverageScore(), resilientScoreComparator)
            .toComparison();
}

变成

public int compare(SingleBenchmarkResult a, SingleBenchmarkResult b) {
    return Comparator
            .comparing(SingleBenchmarkResult::hasAnyFailure, Comparator.reverseOrder()) // Reverse
            .thenComparing(SingleBenchmarkResult::getAverageScore, resilientScoreComparator)
            .compare(a, b);
}

【问题讨论】:

    标签: intellij-idea java-8 comparator apache-commons-lang apache-commons-lang3


    【解决方案1】:

    如果你这样写

    public int compare(Person a, Person b) {
        return Comparator
                .comparing(Person::getLastName)
                .thenComparing(Person::getFirstName)
                .compare(a, b);
    }
    

    您通过为每次比较构建一个新的Comparator 来浪费性能。并且在查看周围的代码时应该显然是无意义的。 compare(Person a, Person b) 方法肯定是实现Comparator<Person> 的类的一部分,您可以在某个地方实例化它以获得所需的比较器。您应该将 那个 实例替换为唯一的 Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName) 实例,用于整个操作。

    例如

    // reusable
    static final Comparator<Person> By_NAME = Comparator
                 .comparing(Person::getLastName).thenComparing(Person::getFirstName);
    

    或临时

    listOfPersons.sort(Comparator.comparing(Person::getLastName)
                                 .thenComparing(Person::getFirstName));
    

    如果你以这种方式使用它,它很可能会更快。但是,您应该看到,不可能有简单的基于模式的替换。您必须用那个简单的声明性构造替换类的使用站点,并决定是为多个使用站点使用共享比较器实例还是临时创建它。然后,您可以删除整个旧的实现类,或者至少,如果它仍然用于其他目的,则从中删除比较器功能。

    【讨论】:

    • 我需要实现 Person 的自然比较,所以我无法应用这些建议的更改。您是否认为 Comparator.comparing(...) 也比 CompareToBuilder 慢?
    • 如果你实现自然顺序,方法应该是compareTo(Person)而不是compare(Person,Person),所以这个问题有误导性。我不认为使用Comparator.comparing 会比CompareToBuilder 慢,但仍然可以通过声明static final 字段来改进它,如我的答案所示并将compareTo(Person) 实现为return BY_NAME.compare(this,other);
    • 好点。实际上,我有自然顺序的用例,也有可重用 Comperator 的用例(如上)。我明白为什么不再需要将专门的 Comperator 设为单独的类了。
    【解决方案2】:

    我认为没有任何预定义的检查。您可能会尝试使用 IntelliJ 的 structural-search,尽管我认为对每种可能的情况都这样做可能会非常棘手。具有两个比较的简单案例的一种可能性可能如下:

    搜索模板($TYPE$$z$ 的出现次数为 2):

    $ReturnType$ $MethodName$($TYPE$ $z$) {
            return new CompareToBuilder()
                    .append($A$.$m$(), $B$.$m$())
                    .append($A$.$m1$(), $B$.$m1$())
                    .toComparison();
        }
    

    替换模板:

    $ReturnType$ $MethodName$($TYPE$ $z$) {
        return java.util.Comparator
                .comparing($TYPE$::$m$)
                .thenComparing($TYPE$::$m1$)
                .compare($A$, $B$);
    }
    

    我不是结构搜索方面的专家,但我想您必须为具有或多或少比较的调用创建另一种模式。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-25
      • 1970-01-01
      • 2021-11-01
      • 2018-04-08
      • 2015-01-14
      • 2012-09-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多