2009-08-11 7 views
7

Ich schreibe einige benutzerdefinierte Vergleicher, und ich möchte, dass sie null Elemente an den unteren Rand der Liste schieben, unabhängig davon, ob ich aufsteigend oder absteigend sortiere. Was ist eine gute Strategie oder ein gutes Muster dafür?Schöne allgemeine Art, Nullen ganz nach unten zu sortieren, egal?

Offhand:

  • Schreiben Sie einfach getrennt auf- und absteigend Komparatoren, Code-Sharing wo möglich
  • Delegate null Handhabung an einen anderen Klasse, entweder durch eine NPE oder werfen, indem sie explizit
  • Aufruf
  • Fügen Sie ein aufsteigendes Flag hinzu und setzen Sie bedingte Logik darin, um um die Nullen
  • zu navigieren
  • Wrap regelmäßige Komparatoren in einem null Handling-Klasse

Jede andere Strategien? Ich würde gern von irgendwelchen Erfahrungen mit verschiedenen Ansätzen und Fallstricken für die verschiedenen Strategien hören.

Antwort

5

Die letzte Option gefällt mir sehr. Komparatoren sind wirklich großartig, um zusammen zu ketten. Insbesondere möchten Sie vielleicht eine ReverseComparator sowie eine NullWrappingComparator schreiben.


EDIT: Sie müssen dies nicht selbst schreiben. Wenn man sich die Ordering Klasse aussehen in der Google Collections Library werden Sie diese und alle möglichen anderen Leckereien finden :)


EDIT: mehr ins Detail gehen zu zeigen, was ich meine über ReverseComparator ...

Ein Wort der Warnung - in der Implementierung eines ReverseComparator, umgekehrte Reihenfolge der Argumente, anstatt das Ergebnis zu negieren, wie sonst Integer.MIN_VALUE ist "umgekehrt" zu sich selbst.

Also diese Implementierung falsch ist (original Annahme, daß der Komparator umgekehrt):

public int compare(T x, T y) 
{ 
    return -original.compare(x, y); 
} 

aber das ist richtig:

public int compare(T x, T y) 
{ 
    return original.compare(y, x); 
} 

Der Grund dafür ist, dass wir den Vergleich immer umkehren wollen, aber wenn zurückgibt, dann wird der schlechte Vergleicher auch Rückkehr int.MIN_VALUE, die falsch ist. Dies ist aufgrund der witzigen Eigenschaft, dass int.MIN_VALUE == -int.MIN_VALUE.

+0

Ich stimme Ihrer Antwort zu, außer dass Sie das Ergebnis eines Vergleichs negieren. Ich wäre ziemlich unglücklich, wenn ich herausfände, dass ein Comparator einen ganzzahligen Vergleich mit Subtraktion durchführt, da diese Methode so viele Fallstricke hat. – jprete

+1

@jprete: Ich glaube, du hast mich falsch verstanden. Ich werde bearbeiten. –

+1

Ich akzeptiere diese Antwort aufgrund des Verweises auf die Bestellklasse von Google Collections. Bestehender bewährter Code ist die beste Lösung. Auch für die Warnung über Integer.MIN_VALUE. Aber ich schätze den @ dfa-Code unten sehr und wünschte, ich könnte beide Antworten akzeptieren. –

10

Ich stimme Jon Skeet (es ist so einfach :).Ich habe versucht, eine sehr einfache decorator zu implementieren:

class NullComparators { 

    static <T> Comparator<T> atEnd(final Comparator<T> comparator) { 
     return new Comparator<T>() { 

      public int compare(T o1, T o2) { 
       if (o1 == null && o2 == null) { 
        return 0; 
       } 

       if (o1 == null) { 
        return 1; 
       } 

       if (o2 == null) { 
        return -1; 
       } 

       return comparator.compare(o1, o2); 
      } 
     }; 
    } 

    static <T> Comparator<T> atBeginning(final Comparator<T> comparator) { 
     return Collections.reverseOrder(atEnd(comparator)); 
    } 
} 

gegeben ein Vergleicher:

Comparator<String> wrapMe = new Comparator<String>() { 
     public int compare(String o1, String o2) { 
      return o1.compareTo(o2); 
     } 
}; 

und einige Testdaten:

List<String> strings = Arrays.asList(null, "aaa", null, "bbb", "ccc", null); 

Sie mit Nullen am Ende sortieren:

Collections.sort(strings, NullComparators.atEnd(wrapMe)); 

oder am Anfang:

Collections.sort(strings, NullComparators.atBeginning(wrapMe)); 
 
[null, null, null, ccc, bbb, aaa] 
+3

Sehr schön! Vielen Dank. Gibt es einen Grund dafür, 0 nicht zurückzugeben, wenn beide Argumente null sind? Ich weiß, dass Null-Verhalten ist nicht ganz wie Nicht-Null-Verhalten, und dass zwei Nullen sagen gleich ist, ist fraglich - aber ist es weniger fraglich zu sagen, dass man das andere übertrifft? –

+2

@Carl: Genau der Punkt, den ich gerade zu Ihrem Post gemacht habe :) Ein Comparator * sollte * 0 zurückgeben oder eine Ausnahme werfen, wenn zwei Nullen übergeben werden, andernfalls ist es nicht der Schnittstellenvertrag. –

5

Im Anschluss an DFA Antwort auf - was ich will, ist, dass die nulls Art am Ende ohne die Reihenfolge der Nicht-NULL-Werte zu beeinflussen. Deshalb möchte ich etwas mehr nach dem Vorbild dieses:

public class NullComparatorsTest extends TestCase { 
    Comparator<String> forward = new Comparator<String>() { 
            public int compare(String a, String b) { 
             return a.compareTo(b); 
            } 
           }; 

    public void testIt() throws Exception { 
     List<String> strings = Arrays.asList(null, "aaa", null, "bbb", "ccc", null); 
     Collections.sort(strings, NullComparators.atEnd(forward)); 
     assertEquals("[aaa, bbb, ccc, null, null, null]", strings.toString()); 
     Collections.sort(strings, NullComparators.atBeginning(forward)); 
     assertEquals("[null, null, null, aaa, bbb, ccc]", strings.toString()); 
    } 
} 

public class NullComparators { 
    public static <T> Comparator<T> atEnd(final Comparator<T> comparator) { 
     return new Comparator<T>() { 
      public int compare(T a, T b) { 
       if (a == null && b == null) 
        return 0; 
       if (a == null) 
        return 1; 
       if (b == null) 
        return -1; 
       return comparator.compare(a, b); 
      } 
     }; 
    } 

    public static <T> Comparator<T> atBeginning(final Comparator<T> comparator) { 
     return new Comparator<T>() { 
      public int compare(T a, T b) { 
       if (a == null && b == null) 
        return 0; 
       if (a == null) 
        return -1; 
       if (b == null) 
        return 1; 
       return comparator.compare(a, b); 
      } 
     }; 
    } 
} 

Volle Gutschrift auf DFA, obwohl - das ist nur eine kleine Änderung seiner Arbeit.

+1

Ein Problem: Sie geben 0 nicht zurück, wenn Sie zwei Nullen vergleichen. –

+0

Danke; bearbeitet mit Korrektur. –

2

Sie könnten immer NullComparator von Commons-Sammlungen verwenden. Es ist länger als Google Sammlungen.

3

In Java 8 können Sie die statischen Methoden und Comparator.nullsFirst verwenden, um mehr Null-freundliche Komparatoren zu haben. Angenommen, Sie haben eine Fruit Klasse wie folgt aus:

public class Fruit { 
    private final String name; 
    private final Integer size; 

    // Constructor and Getters 
} 

Wenn Sie durch ihre Größe ein paar Früchte sortieren und legte die null s am Ende:

List<Fruit> fruits = asList(null, new Fruit("Orange", 25), new Fruit("Kiwi", 5)); 

Sie können einfach schreiben:

Collections.sort(fruits, Comparator.nullsLast(Comparator.comparingInt(Fruit::getSize))); 

Und das Ergebnis wäre:

[Fruit{name='Kiwi', size=5}, Fruit{name='Orange', size=25}, null]