Sort with one element at the end

问题: I have a list of objects and I want to sort it alphabetically by an attribute. But I want to add an exception rule if this attribute matches a specific string. For example:...

问题:

I have a list of objects and I want to sort it alphabetically by an attribute. But I want to add an exception rule if this attribute matches a specific string. For example:

public class Car {
  String name;
}
List<Car> cars = asList(
    new Car("Unassigned"), 
    new Car("Nissan"), 
    new Car("Yamaha"), 
    new Car("Honda"));

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

If cars.name == "Unassigned" then this car should remain at the end of the list and the result would be:

[Car<Honda>, Car<Nissan>, Car<Yamaha>, Car<Unassigned>]

回答1:

  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...


回答2:

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 prittier, 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(Collectors.toList());

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


回答3:

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

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

回答4:

Alternatively, just remove all instances of "Unassigned" and add however many to the end of the List after. For example:

int numberOfUnassigned = 0;
for (Iterator<Car> iterator = sortedCars.iterator(); iterator.hasNext();) {
    String str = iterator.next().getName();
    if (str.equals("Unassigned")) {
        iterator.remove();
        numberOfUnassigned++;
    }
}

And then add the number of numberOfUnassigned to the end.


回答5:

One way to possibly do that would be using partitioning as:

Map<Boolean, List<Car>> partitionedCars = cars
        .stream()
        .collect(Collectors.partitioningBy(a -> a.getName().equals("Unassigned")));

List<Car> sortedCars = Stream.concat(partitionedCars.get(Boolean.FALSE).stream()
        .sorted(Comparator.comparing(Car::getName)), partitionedCars.get(Boolean.TRUE).stream())
        .collect(Collectors.toList());
  • 发表于 2019-03-12 23:14
  • 阅读 ( 170 )
  • 分类:sof

条评论

请先 登录 后评论
不写代码的码农
小编

篇文章

作家榜 »

  1. 小编 文章
返回顶部
部分文章转自于网络,若有侵权请联系我们删除