so, there is a class called City which has two fields (Name and Population) and the usual getters and setters. now my question is why can i sort cities by name just fine like this
public class Main{
public static void main(String[] args) {
List<City> list = List.of(new City("Tokyo", 7), new City("Mexico City", 1),
new City("São Paulo", 3), new City("Lagos", 5),
new City("Istanbul", 4), new City("Sydney", 6),
new City("McMurdo", 2));
List<String> listOfNames = list.stream().sorted((City o1, City o2) -> o1.getName().compareTo(o2.getName())).map(City -> City.getName()).collect(Collectors.toList());
listOfNames.forEach(Element -> System.out.println(Element));
}
}
but I can only sort by population like this(in descending order)
List<String> listOfNames = list.stream().sorted((City o1, City o2) -> o2.getPopulation() - o1.getPopulation()).map(City -> City.getName()).collect(Collectors.toList());
Why is this approach wrong
List<String> listOfNames = list.stream().sorted((City o1, City o2) -> Comparator.comparingInt(o1.getPopulation(), o2.getPopulation())).map(City -> City.getName()).collect(Collectors.toList());
why can't I use compare/compareTo like i did when sorting be name(String)
PS
I did not implement comparable or have a Comparator in my code
Sorting only works if the elements you try to sort implement the Comparable
interface or provide a Comparator
implementation that accepts your elements. String
already implements this interface, so you can just sort on it without thinking about it.
Your last example should be like this, so you generate a Comparator
on the fly with the population as a comparison basis:
list.stream().sorted(Comparator.comparingInt(City::getPopulation)).map(City -> City.getName()).collect(Collectors.toList())
You can also implement Comparable
with City
but then you need to decide how to compare Name and Population at the same time (or just compare by Population inside the Comparable
implementation and do manual sort for Name.)
In order to be able to sort objects they either have to implement Comparable
interface, in this case objects themself aware how they need to be compared, or provide an instance of Comparator
.
And obviously any instance comparator is an implementation of Comparator
interface, ie it has an implementation of compare()
method.
While using Java 8 static methods from the Comparator
interface like comparing()
, comparingInt()
, etc. you need to provide as an argument a Java 8 function , which allows to extract a value from your object that is already comparable, an you whill be given an instance of Comparator
with its compare()
method implemented according to the provided function.
In other words, Java 8 brought us some convenience methods, but the general contract is still there .
The following implementation of comparator
(City o1, City o2) -> o2.getPopulation() - o1.getPopulation()
is the same as
Comparator.comparingInt(City::getPopulation)
The latter is written in a more concise form. And it's a preferred way of implementing Comparator
in such case.
But what you definitely shouldn't do is to write the code like that (snippet is based the OP's very last example, which fails to compile):
(City o1, City o2) -> Comparator.comparingInt(City::getPopulation).compare(o1, o2)
It would work, but it defeats the purpose of the static convenience methods - the code is ugly and redundant. Comparator.comparingInt
already returns a comparator - use it.
You can also use Integer.compare
List<String> listOfNames = list.stream().sorted((City o1, City o2) -> Integer.compare(o1.getPopulation(), o2.getPopulation())).map(City -> City.getName()).collect(Collectors.toList());
String implements the Comparable interface, which specifies how to sort Strings. Integer also implements the Comparable interface, so it is possible to use 'compareTo' with Integers. But i'm assuming that population will be a primitive of the type 'int'. That is the reason you cannot use compareTo. When working with primitives or classes that do not implement Commparable or when you want to deviate from natural sorting, you have to provide a comparator and specify how you want to sort.
You can sort the stream like so:
.sorted(Comparator.comparingInt(city -> city.getPopulation()))
I personally prefer using the method reference:
.sorted(Comparator.comparingInt(City::getPopulation))
There is also no reason to use the compareTo method in the Stream sorted method. If your Class implements the Comparable method you can simply use the '.sorted()' method to use the natural (default) sorting specified in the compareTo method. For specific sorting, you can always use Comparator.comparing(), .thenComparing(), .reversed() or other convenient static methods provided by Comparator.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.