Цепочка компараторов в java

чтение Java учебник Oracle по интерфейсам что дает пример Card (игральные карты) я пытался понять методы по умолчанию, в интерфейсах. Вот ссылке раздел "интеграция методов по умолчанию в существующие интерфейсы". Теперь в последнем разделе они отсортировали карты сначала по рангу, а затем по мастям. Были приведены следующие логики. Предположим, что все интерфейсы, функции или классы, которые используются определено и принимает Comparator

логика 1:

package defaultmethods;

import java.util.*;
import java.util.stream.*;
import java.lang.*;

public class SortByRankThenSuit implements Comparator<Card> {
    public int compare(Card firstCard, Card secondCard) {
        int compVal =
            firstCard.getRank().value() - secondCard.getRank().value();
        if (compVal != 0)
            return compVal;
        else
            return firstCard.getSuit().value() - secondCard.getSuit().value(); 
    }
}

логика 2:

myDeck.sort(
    Comparator
        .comparing(Card::getRank)
        .thenComparing(Comparator.comparing(Card::getSuit)));

теперь у меня возникли некоторые проблемы в понимании второй логики. Я читал компаратор интерфейсы и новые статические методы, которые были включены в Java 1.8 . Теперь я понимаю, что-то вроде этого myDeck.sort(Comparator.comparing(Card::getRank)) который сортируется по рангу, но после чтения документация thenComparing , я не могу понять, как thenComparing возвращает a Comparator который достигает вышеуказанной логики 1. Он внутренне строит что-то вроде if-else строительство, как указано в логике 1 ?

2 ответов


Да, он создает внутренне что-то подобное, только с более промежуточных лямбды. Предполагая, что ваш getRank и getSuit методы возвращают некоторые экземпляры сопоставимых классов Rank и Suit в вашем случае вы эффективно иметь:

Function<Card, Rank> toRank = Card::getRank;
Comparator<Card> comp1 = (a, b) -> toRank.apply(a).compareTo(toRank.apply(b));
Function<Card, Suit> toSuit = Card::getSuit;
Comparator<Card> comp2 = (a, b) -> toSuit.apply(a).compareTo(toSuit.apply(b));
Comparator<Card> result = (a, b) -> {
  int res = comp1.compare(a, b);
  return res != 0 ? res : comp2.compare(a, b);
};

поэтому после вставки (которая может быть выполнена JIT-компилятором) у вас может быть что-то вроде этого:

Comparator<Card> result = (a, b) -> {
  int res = a.getRank().compareTo(b.getRank());
  return res != 0 ? res : a.getSuit().compareTo(b.getSuit());
};

обратите внимание, что вы можете использовать простой вариант:

myDeck.sort(
    Comparator
        .comparing(Card::getRank)
        .thenComparing(Card::getSuit));

это реализация на моей машине (Oracle JDK 8u40)

default Comparator<T> thenComparing(Comparator<? super T> other) {
    Objects.requireNonNull(other);
    return (Comparator<T> & Serializable) (c1, c2) -> {
        int res = compare(c1, c2);
        return (res != 0) ? res : other.compare(c1, c2);
    };
}

Так что да, это по существу, если else (точнее, троичный оператор).