2009-08-11 7 views
7

Sto scrivendo alcuni Comparatori personalizzati e mi piacerebbe che spingessero gli oggetti nulli in fondo alla lista, indipendentemente dal fatto che io stia salendo o scendendo. Qual è una buona strategia o modello per avvicinarsi a questo?Un modo semplice ed intuitivo di ordinare i null in basso, indipendentemente?

Lì per lì:

  • Basta scrivere ascendenti e decrescente comparatori separati, la condivisione di codice ove possibile
  • delegato la gestione null per un'altra classe , sia lanciando una NPE o chiamando esplicitamente
  • Includere un flag crescente e inserire la logica condizionale per navigare attorno ai valori nulli
  • Wrap comparatori regolari in una classe null-movimentazione

Eventuali altre strategie? Mi piacerebbe sapere di eventuali esperienze con approcci diversi e qualsiasi insidia per le varie strategie.

risposta

5

L'ultima opzione mi piace molto. I comparatori sono davvero fantastici da mettere insieme. In particolare potresti voler scrivere uno ReverseComparator e uno NullWrappingComparator.


MODIFICA: non è necessario scrivere da soli. Se guardate la classe Ordering nel Google Collections Library troverete questo e tutti i tipi di altre chicche :)


EDIT: Andando più nel dettaglio per mostrare cosa intendo quando parlo ReverseComparator ...

Un avvertimento: nell'implementazione di un ReverseComparator, invertire l'ordine degli argomenti invece di annullare il risultato, altrimenti Integer.MIN_VALUE è "invertito" a se stesso.

Quindi questa implementazione è sbagliato (supponendo original è il comparatore di invertire):

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

ma questo è giusto:

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

La ragione è che abbiamo sempre voglia di invertire il confronto, ma se original.compare(x, y) restituisce int.MIN_VALUE, il confronto errato sarà anche restituito int.MIN_VALUE, che non è corretto. Ciò è dovuto alla proprietà divertente che int.MIN_VALUE == -int.MIN_VALUE.

+0

Sono d'accordo con tutte le tue risposte tranne che sulla negazione del risultato di un confronto; Sarei abbastanza infelice se scoprissi che un comparatore stava implementando il confronto tra interi utilizzando la sottrazione, dal momento che quel metodo ha così tante insidie. – jprete

+1

@jprete: Penso che tu mi abbia frainteso. Io modifico –

+1

Accetto questa risposta a causa del riferimento alla classe Ordini di Google Collections: il codice comprovato esistente è la soluzione migliore. Anche per l'avviso su Integer.MIN_VALUE. Ma apprezzo molto il codice di @ dfa, di seguito, e vorrei poter accettare entrambe le risposte. –

10

Sono d'accordo con Jon Skeet (è così facile :).Ho cercato di implementare un semplice decorator:

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

dato un comparatore:

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

e alcuni dati di test:

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

è possibile ordinare con valori nulli del fondo:

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

o all'inizio:

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

Molto bello! Grazie. C'è un motivo per non restituire 0 se entrambi gli argomenti sono nulli? So che il comportamento nullo non è proprio come un comportamento non nullo, e che dire che due valori nulli sono uguali è discutibile - ma è meno discutibile dire che uno supera l'altro? –

+2

@Carl: esattamente il punto che ho appena aggiunto al tuo post :) Un comparatore * dovrebbe * restituire 0 o generare un'eccezione quando ha passato due valori null, altrimenti disobbedisce al contratto dell'interfaccia. –

5

Facendo seguito alla risposta di dfa - quello che voglio è che i valori nulli sorta alla fine senza alterare l'ordine dei non nulli. Quindi voglio qualcosa di più lungo le linee di questo:

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

credito completo a DFA, anche se - questo è solo una piccola modifica del suo lavoro.

+1

Un problema: non si restituisce 0 quando si confrontano due valori null. –

+0

Grazie; modificato con correzione. –

2

È sempre possibile utilizzare NullComparator da commons-collections. È stato più lungo di Google Collections.

3

In Java 8, è possibile utilizzare i metodi statici Comparator.nullsLast e Comparator.nullsFirst per avere più comparatori nulle. Supponiamo di avere una classe Fruit come la seguente:

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

    // Constructor and Getters 
} 

Se si desidera ordinare un po 'di frutta per le loro dimensioni e mettere i null s alla fine:

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

Si può semplicemente scrivere:

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

E il risultato sarebbe:

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