0

Model example:

I need to sort stream of cars based on gender of the driver and then his age.

public class Car {
    String color;
    Driver driver; 
}
public class Driver {
    String name;
    Integer age;
    String gender;  
}
public List<Car> sortCars(List<Car> cars) {
    return cars.stream()
        .sorted(Comparator
                .comparing(car -> -1 /* car.getDriver().getGender() */ )
                .thenComparing(car -> -1 /* car.getDriver().getAge() */ ))
        .collect(Collectors.toList());
}

I do not want to create special Comparator classes or implement Comparator interfaces. Is my use case even possible? I've been browsing SO for the past hour and none of the answers work.

2 Answers 2

2

Probably the type inference isn't strong enough. This worked for me:

return cars.stream()
        .sorted(Comparator.comparing(car -> ((Car) car).getDriver().getGender())
                .thenComparingInt(car -> ((Car) car).getDriver().getAge()))
        .collect(Collectors.toList());
Sign up to request clarification or add additional context in comments.

2 Comments

Don't use cast, it prevents type safety. Instead, specify the parameter type: Comparator.comparing((Car car) -> car.getDriver().getGender()). or specify the type parameter: Comparator.<Car, String>comparing(car -> car.getDriver().getGender())
@Andreas I've seen and tried this solution, but it didn't work for some reason. Tried it again and now there are no complaints in IDE, thank you! I really like your solution.
1

The answer by @Kartik works. But if you want to avoid casting, you have two more options:

Option 1:

 Comparator<Car> com1 = Comparator.comparing(car -> car.getDriver().getGender());
 Comparator<Car> com2 = Comparator.comparing(car -> car.getDriver().getAge());

then:

cars.stream().sorted(com1.thenComparing(com2)).collect(Collectors.toList());

Option 2:

Let the compiler know you are comparing Cars:

cars.stream().sorted(Comparator.comparing((Car car) -> car.getDriver().getGender())
    .thenComparing(car -> car.getDriver().getAge()))
                    .collect(Collectors.toList());

EDIT: Actually, you have even more options. Per @Andreas' comment, you can pass the type by Comparator.<Car, String>comparing or you can just write your own comparing function which will look like:

sorted((car1, car2) -> {/*function body that returns -1, 0, 1 */}

Also, if you have a List<Car> you want to sort, you can directly use sort() method:

cars.sort(com1.thenComparing(com2));

3 Comments

You don't have to specify the parameter type in thenComparing.
I actually used sorted((car1, car2) -> {/*function body that returns -1, 0, 1 */} as a temporary workaround. Option 1 makes sense in the cases, where you need to reuse the comparators, but that was not my case unfortunately.
@TomášMrázek You don't have to define a global scope for com1 and com2 if you don't want to use them outside of a local scope. In any case, I think this is more readable than casting and unlike casting, it is type safe.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.