Замените CompareToBuilder на Comperator.comparing в Java 8 (...). ThenComparing(...)

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

2 ответа

Если вы напишите это так

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

Если вы используете его таким образом, скорее всего, это будет быстрее. Тем не менее, вы должны увидеть, что не существует простой замены на основе шаблонов. Вы должны заменить сайты использования класса этой простой декларативной конструкцией и принять решение, использовать ли общий экземпляр компаратора для сайтов многократного использования или создать его в режиме ad-hoc. Затем вы можете удалить весь старый класс реализации или, по крайней мере, удалить из него функциональность компаратора, если он все еще служит другим целям.

Я не думаю, что для этого есть какая-то заранее определенная проверка. Вы можете попытаться использовать структурный поиск IntelliJ, хотя я думаю, что это может быть довольно сложно сделать это для каждого возможного случая. Одна возможность для простого случая с двумя сравнениями может быть следующей:

шаблон поиска (количество событий $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$);
}

Я не эксперт по структурному поиску, но я полагаю, вам придется создать другой шаблон для вызовов с более или менее сравнениями.

Другие вопросы по тегам