Подтвердить что ты не робот

Сортировка объектов Java с использованием нескольких ключей

У меня есть коллекция объектов Duck, и я бы хотел сортировать их с помощью нескольких клавиш.

class Duck {
    DuckAge age; //implements Comparable
    DuckWeight weight; //implements Comparable
    String name;
}
List<Duck> ducks = Pond.getDucks();

например. Я хочу отсортировать их в первую очередь по их весам и во второй раз по возрасту. Если две утки имеют одинаковый вес и точный возраст, то пусть они дифференцируют их, используя их имена в качестве третичного ключа. Я мог бы сделать что-то вроде этого:

Collections.sort(ducks, new Comparator<Duck>(){
    @Override
    public int compare(Duck d1, Duck d2){
        int weightCmp = d1.weight.compareTo(d2.weight);
        if (weightCmp != 0) {
            return weightCmp;
        }
        int ageCmp = d1.age.compareTo(d2.age);
        if (ageCmp != 0) {
            return ageCmp;
        }
        return d1.name.compareTo(d2.name);
    }
});

Хорошо, я делаю это довольно часто, но это решение не пахнет правильно. Он плохо масштабируется, и его легко испортить. Разумеется, должен быть лучший способ сортировки уток с помощью нескольких ключей! Кто-нибудь знает о лучшем решении?

EDIT удалены ненужные ветки else

4b9b3361

Ответ 1

Решение Java 8:

Comparator<Duck> cmp = Comparator.comparing(Duck::getWeight)
    .thenComparing(Duck::getAge)
    .thenComparing(Duck::getName);

Ура для лямбда, ссылки на методы и методы по умолчанию:)! Жаль, что мы должны определить геттеры или использовать явные lambdas, например:

Comparator<Duck> cmp = Comparator
    .comparing((Duck duck)-> duck.weight)
    .thenComparing((Duck duck)-> duck.age)
    .thenComparing(duck-> duck.name);

Ввод типа не будет работать с неявными lambdas, поэтому вам нужно указать тип аргумента первых двух lambdas. Подробнее в этом ответе Брайана Гетца.

Ответ 2

Guava является более элегантным:

return ComparisonChain.start()
     .compare(d1.weight, d2.weight)
     .compare(d1.age, d2.age)
     .compare(d1.name, d2.name)
     .result();

Apache commons-lang имеет аналогичную конструкцию, CompareToBuilder.

Ответ 3

List<Duck> ducks = new ArrayList<Duck>();
Collections.sort(ducks, new Comparator<Duck>() {

  @Override
  public int compare(Duck o1, Duck o2) {

    return new org.apache.commons.lang.builder.CompareToBuilder().
        append(o1.weight, o2.weight).
        append(o1.age, o2.age).
        append(o1.name, o2.name).
        toComparison();
  }
});

Ответ 4

Во-первых, ваше решение не так медленно.

Если вам действительно нужен другой метод, дайте каждой утке "оценку", которая по сути представляет собой единственное число, которое является суммой их трех характеристик, но с огромным весом (извините почти неизбежный каламбур) за вес, меньшим один для возраста; и очень маленькое имя.

Вы можете выделить ~ 10 бит для каждой характеристики, поэтому для каждого признака вы должны находиться в диапазоне 0..1023.

score = ( (weight << 10) + age) << 10 + name;

Это, вероятно, совершенно ненужно, но что угодно:)

Ответ 5

Вы можете использовать CompareToBuilder из Apache Commons Lang. (Это объясняет сопоставимые, но работает и для компаратора).

Ответ 7

Я только что переписал ваш код без вложенных инструкций. Вам сейчас нравится?

@Override
public int compare(Duck d1, Duck d2){
    int weightCmp = d1.weight.compareTo(d2.weight);
    if (weightCmp != 0) {
        return weightCmp;
    }
    int ageCmp = d1.age.compareTo(d2.age);
    if (ageCmp != 0) {
        return ageCmp;
    } 

    return d1.name.compareTo(d2.age);
}