Множество критериев сортировки списка объектов с помощью Guava Ordering

У меня есть класс, который не может реализовать сопоставимый, но должен быть отсортирован на основе 2 полей. Как я могу достичь этого с Guava?

Скажем, класс:

class X {
  String stringValue;
  java.util.Date dateValue;
} 

И у меня есть список этих:

List<X> lotsOfX;

Я хочу отсортировать их сначала по полю значения, а затем по убыванию dateValue в каждой "группе" полей "значения".

Что я делал до сих пор:

List<X> sortedList = ImmutableList.copyOf(Ordering.natural().onResultOf(dateValueSortFunction).reverse().sortedCopy(lotsOfX));
sortedList = ImmutableList.copyOf(Ordering.natural().onResultOf(stringValueSortFunction).sortedCopy(sortedList));

Функции определены как:

public class DateValueSortFunction<X> implements Function<X, Long> {

    @Override
      public Long apply(X input) {
        return input.getDateValue().getTime();  //returns millis time
      }
}

А также:

public class StringValueSortFunction<X> implements Function<X, Integer> {

      @Override
        public Integer apply(X input) {
          if(input.getStringValue().equalsIgnoreCase("Something"))
            return 0;
          else if(input.getStringValue().equalsIgnoreCase("Something else"))
            return 1;
          else
            return 2;
        }
}

Ожидаемый результат в sortedList:

Something   03/18/2013
Something   03/17/2013
Something else  03/20/2013
Something else  03/19/2013
....

Мой подход работает, но, очевидно, неэффективен для обхода списка дважды. Есть ли лучший способ сделать это?

Я использую это в приложении GWT. Реализация сопоставимых не вариант.

Ответ 1

Я подозреваю, что вы хотите Ordering.compound. Вы можете сделать все это в одном заявлении, но я бы использовал:

Ordering<X> primary = Ordering.natural().onResultOf(stringValueSortFunction);
Ordering<X> secondary = Ordering.natural()
                              .onResultOf(dateValueSortFunction)
                              .reverse();
Ordering<X> compound = primary.compound(secondary);

List<X> sortedList = compound.immutableSortedCopy(lotsOfX);

Ответ 2

Менее функциональное, но, возможно, более чистое решение:

new Ordering<X>() {
  public int compare(X x1, X x2) {
    return ComparisonChain.start()
      .compare(x1.stringValue, x2.stringValue)
      .compare(x2.dateValue, x1.dateValue) // flipped for reverse order
      .result();
  }
}.immutableSortedCopy(listOfXs);

Ответ 3

Java 8 предоставляет методы для компаратора для краткого описания цепных компараторов. Вместе с недавно представленным List.sort вы можете:

lotsOfX.sort(
    Comparator.comparingInt(x -> stringValueSortFunction.apply(x.stringValue))
        .thenComparing(x -> x.dateValue, Comparator.reverseOrder()));

Это, конечно, мутирует список - сначала сделайте копию, если вы хотите оставить исходный список без изменений или обернуть компаратор в Ordering и использовать immutableSortedCopy, если вы хотите неизменную копию.