简体   繁体   中英

Java sorting using lambda with streams

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM