2013-03-20 6 views
10

Mam klasę, KTÓRA NIE MOŻE wdrożyć porównywalnej, ale musi być posortowana na podstawie 2 pól. Jak mogę to osiągnąć z Guava?Wielokryterialne sortowanie listy obiektów z zamówieniem Guava

Załóżmy, że klasa jest

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

i mam listę tych

List<X> lotsOfX; 

chcę sortować je na podstawie polu wartości, a następnie w oparciu o DATEVALUE zstępującego ciągu każdego " grupy "z pól" wartości.

co robiłem do tej pory jest

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

funkcje są zdefiniowane jako:

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

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

i

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; 
     } 
} 

oczekiwany wynik w SortedList jest

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

Moje podejście działa, ale jest oczywiście niewydajne do dwukrotnego przeglądania listy. Czy jest lepszy sposób na zrobienie tego?

Uwaga: użycie tego w aplikacji GWT. Wdrażanie porównywalnych opcji nie jest możliwe.

Odpowiedz

18

Podejrzewam, że chcesz Ordering.compound. Ci mógłby zrobić to wszystko w jednym rachunku, ale użyję:

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); 
+0

To działa. Dziękuję Ci!! – user949110

+0

Święta krowa! Dzięki za to, uratowałeś mi wiele przypadków IF ... ELSE! Przeżyłem koszmarny okres przejściowy. – Ethenyl

17

Mniej funkcjonalny, ale prawdopodobnie czystsze rozwiązanie:

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); 
+0

Zajrzałem do tego, ale czytałem to. Porównywanie oczekiwałoby danych wejściowych do implementacji porównywalnych. Więc jeśli dobrze rozumiem, w tym przypadku sortowałoby struny alfabetycznie? Ale musiałem wprowadzić własną niestandardową funkcję sortowania. – user949110

+0

Jeśli chcesz posortować 'String's za pomocą niestandardowego komparatora, możesz po prostu użyć polecenia' compare (x1.stringValue, x2.stringValue, new MyCustomStringComparator()) '. –

+0

Rozumiem. Cóż, o ile jeden nie osiąga lepszych wyników niż drugi, z obydwoma jestem w porządku. Myślę, że druga odpowiedź jest akceptowalnie czysta. – user949110

2

Java 8 dostarcza sposobów na komparatora zwięzłego określenia przykuty komparatory. Wraz z nowo wprowadzonym List.sort, można zrobić:

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

Ten mutuje listę, oczywiście - Make kopię pierwszy, jeśli chcesz zostawić oryginalne listy bez zmian, lub owinąć w sposób porównawczy Zamawianie i używanie immutableSortedCopy, jeśli chcesz niezmienną kopię.