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));
Answer from Sweeper on Stack Overflowis
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));
Comparator#compare(T o1, T o2) Compare two objects and returns an integer value based on this criteria:
- A negative value if o1 < o2
- A positive value if o1 > o2
- Zero if they are equal.
Comparator.comparing(Function<? super T, ? extends U> key) returns a Comparator<T> that compares by that sort key.
The main difference is that compare method provides a single point of comparison, whereas comparing chained to other functions to provide multiple points of comparison.
Suppose you have a class Person
public class Person implements Comparable<Person> {
private String firstName;
private String lastName;
private int age;
// rest of class omitted
}
if you compare two Person instances p1 vs p2 using compare(p1, p2), the comparison will be executed and the two objects will be sorted based on some natural ordering prescribed by the class. In contrast, if you want to compare the same two instances using comparing(), the comparison will be executed based on whichever criteria you choose to compare based on some attribute of the class. For example: Comparator.comparing(Person::getFirstName).
Because comparing returns a Comparator rather than a value, as I stated before, you can chain multiple comparisons. For instance: Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName);
As for the meaning of the return type <T, U extends Comparable<? super U>> Comparator<T>, you can find the explanation here.
I want to add that, classes must be comparable in order for compare(T o1, T o2) to work. String objects are comparable because they implement this interface. That said, if a class is not Comparable, you can still use comparing method because as I stated, you get to choose which attribute of the class you would like to use for the comparison and those attributes are likely to be comparable (i.e. String in the case of person's name or age in the above example).
Videos
(The answer to the question is at the end of this post)
Hey guys,
so here's a quote block on the method from the API:
static <T,U extendsComparable<? super U>>Comparator<T> comparing(Function<? super T,? extends U> keyExtractor)Accepts a function that extracts a
Comparablesort key from a typeT, and returns aComparator<T>that compares by that sort key.The returned comparator is serializable if the specified function is also serializable.
API Note: For example, to obtain a
Comparatorthat comparesPersonobjects by their last name,
Comparator<Person> byLastName = Comparator.comparing(Person::getLastName);
To verify what the API said: I created a simple Person class
class Person
{
private String lastName;
public Person(String lastName)
{
this.lastName=lastName;
}
public String getLastName()
{
return lastName;
}
}Then as expected this works:
Comparator<Person> byLastName = Comparator.comparing(Person::getLastName);
---------------------------------------
Q1: But this doesn't work: Comparator<Person> byLastName = Comparator.comparing(person->person.getLastName()); Eclipse doesn't even pick up the method getLastTime(), only Object's methods are present.
Q2: Then I also wondered why does the method done up by the API work? As seen by the method signature <T,U extends Comparable<? super U>>, T is input parameter thus refers to Person type and U is the output parameter thus refers to String type.
However, Person is not a subtype of Comparable<? super String>. Hence, the one in the API shouldn't work too.
My guess:My understanding of the Function<? super T,? extends U> keyExtractor is probably wrong.
-------------------------------------
Anyhow, can some kind soul help out?
Edit:Spelling error
Edit 2: Big thanks to u/knoam (sort of a TLDR for the thread)Answer for Q1: Eclipse is buggy i guess, NetBeans works.Answer for Q2: <T,U extends Comparable <? super U> > made me think that both T and U should be subtypes of Comparable. It's only U.
As far as I know, both are used when it isn't enoguh to compare if it's equal or not, but if it's mor or less than the other one.
But when do you use compareTo() from Comparable over compare() from Comparator? Like, for binary tree you use compare when you add the elements? but for Arrays.sort() you use compareTo(), but why? Like, what's really the difference? From what i've searched up, both compare in the same way but i might be wrong?