As far as I understand Comparator is a functional interface used to compare 2 objects with int compare(T o1, T o2)
as the abstract function that takes two argument.
but there is also a function Comparator.comparing(s->s)
that can take a lambda function with only one input parameter.
for Example to sort a Collection using streams
List<String> projects=Arrays.asList("abc","def","sss","aaa","bbb");
projects.stream().sorted((x,y)->y.compareTo(x)).forEach(s->System.out.println(s));
projects.stream().sorted(Comparator.comparing(s->s)).forEach(s->System.out.println(s));
the sorted method takes a Comparator as a argument. So I am able to understand the first lambda expression but I wonder the use of Comparator.comparing(s->s)
i.e. is Comparator.comparing()
used for converting a single argument lambda expression to a double argument one or does it has some other use as well.
Also please explain the part of the below function declaration.
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
is
Comparator.comparing()
used for converting a single argument lambda expression to a double argument?
Yes, you can sort of think of it like that.
When sorting things, you are supposed to specify "given two things a
and b
, which of them is greater, or are they equal?" using a Comparator<T>
. The a
and b
is why it has 2 lambda parameters, and you return an integer indicating your answer to that question.
However, a much more convenient way to do this is to specify "given a thing x
, what part of x
do you want to sort by?". And that is what you can do with the keyExtractor
argument of Comparator.comparing
.
Compare:
/*
given two people, a and b, the comparison result between a and b is the
comparison result between a's name and b's name
*/
Comparator<Person> personNameComparator =
(a, b) -> a.getName().compareTo(b.getName());
/*
given a person x, compare their name
*/
Comparator<Person> personNameComparator =
Comparator.comparing(x -> x.getName()); // or Person::getName
The latter is clearly much more concise and intuitive. We tend to think about what things to sort by, rather than how exactly to compare two things, and the exact number to return depending on the comparison result.
As for the declaration for comparing
:
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
The <T, U extends Comparable<? super U>>
part first declares two generic type parameters - T
is what the comparator compares (Person
in the above case), and U
is the type that you are actually comparing (String
in the above case), hence it extends Comparable
.
keyExtractor
is the parameter you pass in, such as x -> x.getName()
, that should answer the question of "when given a T
, what is a U
that you want to compare by?".
If you are confused by the ? super
and ? extends
, read What is PECS?.
If you haven't realised already, the implementation of comparing
basically boils down to:
return (a, b) -> keyExtractor.apply(a).compareTo(keyExtractor.apply(b));