Reputation: 1794
Reading the Java Tutorial by Oracle on interfaces which gives a example on Card
(Playing cards) I was trying to understand the default methods in interfaces. Here's the link, section "Integrating default methods in existing interfaces". Now in the last section they sorted the Cards first by rank and then by suits. Following logics have been given. Assume that whatever interfaces, functions or classes that are used have been defined and sort
function takes a Comparator
Logic 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();
}
}
Logic 2:
myDeck.sort(
Comparator
.comparing(Card::getRank)
.thenComparing(Comparator.comparing(Card::getSuit)));
Now I am having some problems in understanding the second logic. I read the comparator interfaces and the new static methods which have been included in Java 1.8 . Now I understand something like this myDeck.sort(Comparator.comparing(Card::getRank))
which sorts by rank but after reading the documentation for thenComparing
, I am unable to understand how thenComparing
returns a Comparator
which achieves the above Logic 1. Does it internally build something like the if-else
construct as specified in Logic 1 ?
Upvotes: 15
Views: 17299
Reputation: 100249
Yes, it creates internally something similar, just with more intermediate lambdas. Assuming that your getRank
and getSuit
methods return some instances of comparable classes Rank
and Suit
, in your case you effectively have:
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);
};
So after inlining (which might be performed by JIT-compiler) you may have something like this:
Comparator<Card> result = (a, b) -> {
int res = a.getRank().compareTo(b.getRank());
return res != 0 ? res : a.getSuit().compareTo(b.getSuit());
};
Note that you can use the simpler version:
myDeck.sort(
Comparator
.comparing(Card::getRank)
.thenComparing(Card::getSuit));
Upvotes: 22
Reputation: 2121
This is the implementation on my machine (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);
};
}
so yes, it is essentially if else (more precisely, the ternary operator).
Upvotes: 12