Sort with one element at the end

  List<Car> sortedCars = cars
        .stream()
        .sorted(Comparator.comparing(
            Car::getName,
            Comparator.comparing((String x) -> x.equals("Unassigned"))
                      .thenComparing(Comparator.naturalOrder())))
        .collect(Collectors.toList());

There are a lot of things going on here. First I am using Comparator.comparing(Function, Comparator); then (String x) -> x.equals("Unassigned") which actually compares a Boolean (that is Comparable); then the fact that (String x) is used - as this type witness is used to correctly infer the types...


The most direct and and easy-to-read solution is probably to write a custom comparator that implements your sorting logic.

You can still use the Comparator.comparing method to make it a bit prettier, though:

public static final String UNASSIGNED = "Unassigned";

List<Car> cars = List.of(
    new Car("Unassigned"), 
    new Car("Nissan"), 
    new Car("Yamaha"), 
    new Car("Honda"));

List<Car> sortedCars = cars.stream()
    .sorted(Comparator.comparing(Car::getName, (name1, name2) -> {
            if (name1.equals(name2)) return 0;
            if (name1.equals(UNASSIGNED)) return 1;
            if (name2.equals(UNASSIGNED)) return -1;
            return name1.compareTo(name2);
    }))
    .collect(toList());

It is possible to extract the "at-the-end" functionality to a separate comparable combinator method. Like this:

List<Car> sortedCars = cars.stream()
    .sorted(Comparator.comparing(Car::getName, withValueAtEnd(UNASSIGNED)))
    .collect(toList());

public static <T extends Comparable<T>> Comparator<T> withValueAtEnd(T atEnd) {
  return withValueAtEnd(atEnd, Comparator.naturalOrder());
}

public static <T> Comparator<T> withValueAtEnd(T atEnd, Comparator<T> c) {
    return (a, b) -> {
        if (a.equals(atEnd)) return 1;
        if (b.equals(atEnd)) return -1;
        return c.compare(a, b);
    };
}

Also, it's good style to use a named constant for special values like your "Unassigned".


Also, note that if you don't need to keep the unsorted cars list, then you can sort that list in place instead of using a stream:

cars.sort(UNASSIGNED_COMPARATOR);

You could just replace "Unassigned" with an end-of-alphabet string in your comparator.

Comparator.comparing(car -> car.getName().equals("Unassigned") ? "ZZZ" : car.getName())