2014-10-14 9 views
5

questo può essere una semplice domanda, ma vorrei capire chiaramente ...Java 8 comparatore nullsFirst naturalOrder confuso

Ho un codice come questo:

public final class Persona 
{ 
    private final int id; 
    private final String name 
    public Persona(final int id,final String name) 
    { 
     this.id = id; 
     this.name = name; 
    } 
    public int getId(){return id;}  
    public String getName(){return name;}  
    @Override 
    public String toString(){return "Persona{" + "id=" + id + ", name=" + name+'}';}  
} 

e sto testando questo codice :

import static java.util.Comparator.*; 
private void nullsFirstTesting() 
{    
    final Comparator<Persona>comparator = comparing(Persona::getName,nullsFirst(naturalOrder())); 
    final List<Persona>persons = Arrays.asList(new Persona(1,"Cristian"),new Persona(2,"Guadalupe"),new Persona(3,"Cristina"),new Persona(4,"Chinga"),new Persona(5,null)); 
    persons 
      .stream() 
      .sorted(comparator) 
      .forEach(System.out::println);       
} 

Questo mostra i seguenti risultati:

Persona{id=5, name=null} 
Persona{id=4, name=Chinga} 
Persona{id=1, name=Cristian} 
Persona{id=3, name=Cristina} 
Persona{id=2, name=Guadalupe} 

Questi risultati vanno bene per me ma ho un problema di comprensione.

Quando ignorare l'oggetto new Persona(5,null) e passo comparatore:

final Comparator<Persona>comparator = comparing(Persona::getName); 

Esso funziona come un fascino. Il mio ordinamento è di natural order of name property. Il problema sorge quando aggiungo l'oggetto con name=null, ho solo pensato che avrei avuto bisogno del mio comparatore come questo.

final Comparator<Persona>comparator = comparing(Persona::getName,nullsFirst()); 

Il mio pensiero era erronea: "OK, quando il nome non è nullo, vengono ordinati in natural order of name, proprio come il comparatore precedente, e se sono null essi saranno prima ma la mia non i nomi null saranno ancora ordinati nell'ordine naturale ".

Ma il codice giusto è questo:

final Comparator<Persona>comparator = comparing(Persona::getName,nullsFirst(naturalOrder())); 

Non capisco il parametro nullsFirst. Ho solo pensato che lo natural order of name avrebbe esplicitamente [predefinito] anche i valori null.

Ma i documenti dicono:

Restituisce un comparatore null-friendly che considera null essere meno di non nullo. Quando entrambi sono null, sono considerati uguali a . Se entrambi non sono nulli, lo Comparator specificato viene utilizzato per determinare l'ordine. Se il comparatore specificato è null, , il comparatore restituito considera tutti i valori non nulli uguali.

Questa riga: "Se entrambi non sono nulli, per determinare l'ordine viene utilizzato lo Comparator specificato."

Sono confuso quando e come l'ordine naturale deve essere impostato in modo esplicito o quando viene inferito.

+3

Nota * "Se il comparatore specificato è 'null', quindi il comparatore restituito considera tutti i valori non nulli uguali." * In altre parole, puoi fare 'nullsFirst (null)'; tuttavia ordina semplicemente i null in primo piano senza ordinare i non null. Il doc in realtà lo spiega abbastanza bene. – Radiodef

+0

@chiperotiz quindi come lo hai risolto alla fine? Sei riuscito a ordinare prima per null e poi per ordine naturale? – Unheilig

+1

sì usando questo codice 'comparatore finale comparatore = confrontando (Persona :: getName, nullsFirst (naturalOrder()));' – chiperortiz

risposta

17

Il comparatore "ordine naturale", che è quello che si ottiene quando si utilizza comparing con un solo parametro, non gestisce null. (Non sono sicuro di dove ti è venuta l'idea.) Il "ordine naturale" di una classe Comparable è definita dal metodo compareTo(), che viene utilizzato in questo modo:

obj1.compareTo(obj2) 

Ovviamente questo non funziona se obj1 è nulla; per String, verrà anche generata un'eccezione di obj2 null.

Il metodo naturalOrder() restituisce un valore Comparator che confronta due oggetti. Il javadoc dice esplicitamente che questo comparatore getta NullPointerException quando si confronta null.

Il metodo nullsFirst() (e nullsLast() allo stesso modo) trasforma sostanzialmente un Comparator in un nuovo Comparator. Si inserisce un comparatore che può generare un'eccezione se tenta di confrontare null e sputa un nuovo comparatore che funziona allo stesso modo, tranne che consente argomenti nulli. Ecco perché è necessario un parametro per nullsFirst - perché crea un nuovo comparatore sopra un comparatore esistente e tu gli dici quale sia il comparatore esistente.

Quindi perché non ti dà l'ordine naturale se si omette il parametro? Perché non l'hanno definito in quel modo. nullsFirst è definita nel javadoc di prendere un parametro:

static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) 

Penso che se i progettisti hanno voluto, avrebbero potuto aggiunto un sovraccarico che non accetta parametri:

static <T> Comparator<T> nullsFirst() // note: not legal 

che sarebbe stato lo stesso come usando nullsFirst(naturalOrder()). Ma non l'hanno fatto, quindi non puoi usarlo in quel modo.

+0

Ciao ajb non avrei mai pensato che l'unico parametro di confronto sarebbe stato gestire null .. ho solo pensato a nullsFirst (Il metodo metterebbe prima i null e i non null sarebbero ordinati in un ordine naturale ... ma come hai detto 'statico Comparator nullsFirst() // note: not legal' simple non è legale. Ho solo pensato che sarebbe bello come hai detto che nullFirst senza parametri dovrebbe essere naturaleOrder() come predefinito .. grazie. – chiperortiz

11

Prova:

final Comparator<Persona> comparator = 
    comparing(Persona::getName, nullsFirst(naturalOrder())); 
1

Ho una lista di Employee con studenti con nome e id ..

import java.util.ArrayList; 
import java.util.Iterator; 

import java.util.List; 
import java.util.Comparator; 

public class TestClass { 

    public static void main(String[] args) { 

     Student s1 = new Student("1","Nikhil"); 
     Student s2 = new Student("1","*"); 
     Student s3 = new Student("1",null); 
     Student s11 = new Student("2","Nikhil"); 
     Student s12 = new Student("2","*"); 
     Student s13 = new Student("2",null); 
     List<Student> list = new ArrayList<Student>(); 
     list.add(s1); 
     list.add(s2); 
     list.add(s3); 
     list.add(s11); 
     list.add(s12); 
     list.add(s13); 

     list.sort(Comparator.comparing(Student::getName,Comparator.nullsLast(Comparator.naturalOrder()))); 

     for (Iterator iterator = list.iterator(); iterator.hasNext();) { 
      Student student = (Student) iterator.next(); 
      System.out.println(student); 
     } 


    } 

} 

produce un output come

Student [name=*, id=1] 
Student [name=*, id=2] 
Student [name=Nikhil, id=1] 
Student [name=Nikhil, id=2] 
Student [name=null, id=1] 
Student [name=null, id=2]