2014-10-14 4 views
5

dies eine einfache Frage sein, aber ich möchte es klar verstehen, ...Java 8 Vergleicher nullsFirst naturalOrder verwirrt

Ich habe einen Code wie folgt:

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

Und teste ich diesen Code Dies zeigt die folgenden Ergebnisse

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

:

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} 

Diese Ergebnisse sind in Ordnung, aber ich habe ein Problem zu verstehen.

Wenn ich ignorieren die new Persona(5,null) Objekt und gebe ich den Komparator:

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

Es funktioniert wie ein Charme. Meine Sortierung ist natural order of name property. Das Problem tritt auf, wenn ich das Objekt mit name=null hinzufüge, dachte ich nur, ich würde meinen Vergleicher so brauchen.

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

Mein Gedanke war irrtümliche: „OK, wenn der Name nicht-null ist, werden sie in natural order of name, genau wie die vorherigen Komparator sortiert, und wenn sie null sind, werden sie zuerst aber meine nicht sein Nullnamen werden weiterhin in natürlicher Reihenfolge sortiert. ".

Aber der richtige Code ist dies:

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

Ich verstehe nicht, den Parameter auf nullsFirst. Ich dachte nur, die natural order of name würde explizit [Standard] sogar null Werte behandeln.

Aber die docs sagen:

Gibt einen Null freundlichen Komparator, die null weniger als nicht-null hält. Wenn beide null sind, gelten sie als gleich. Wenn beide nicht null sind, wird der angegebene Comparator verwendet, um den Auftrag zu bestimmen. Wenn der angegebene Vergleicher null, ist, berücksichtigt der zurückgegebene Vergleicher alle Nicht-Null-Werte als gleich.

Diese Zeile: "Wenn beide nicht null sind, wird die angegebene Comparator verwendet, um die Reihenfolge zu bestimmen."

Ich bin verwirrt, wann und wie die natürliche Reihenfolge explizit festgelegt werden sollte oder wenn sie abgeleitet werden.

+3

Hinweis * "Wenn der angegebene Komparator ist 'null', dann berücksichtigt der zurückgegebene Komparator alle Nicht-Null-Werte als gleich." * Mit anderen Worten, Sie können 'nullsFirst (null)'; es sortiert jedoch nur die Nullen nach vorne, ohne die Nicht-Nullen zu sortieren. Der Doc erklärt es eigentlich ziemlich gut. – Radiodef

+0

@chiperotiz wie hast du es schließlich gelöst? Konnten Sie zuerst nach Nullen sortieren und dann nach Natural order? – Unheilig

+1

ja mit diesem Code 'final Comparator comparator = Vergleichen (Persona :: getName, nullsFirst (naturalOrder()));' – chiperortiz

Antwort

17

Der Komparator "natürliche Reihenfolge", der Sie erhalten, wenn Sie comparing mit nur einem Parameter verwenden, tut nicht Handle Nullen. (Ich bin mir nicht sicher, wo Sie die Idee hatten, dass es so war.) Die „natürliche Ordnung“ eine Comparable Klasse wird durch die compareTo() Methode definiert, die wie folgt verwendet:

obj1.compareTo(obj2) 

Offensichtlich wird dies nicht funktionieren, wenn obj1 null ist; für String, wird es auch eine Ausnahme von obj2 ist Null werfen.

Die Methode naturalOrder() gibt eine Comparator zurück, die zwei Objekte vergleicht. Die javadoc sagt explizit, dass dieser Komparator NullPointerException beim Vergleich Null wirft.

Die nullsFirst() Methode (und nullsLast() ähnlich) transformiert grundsätzlich eine Comparator in eine neue Comparator. Sie fügen einen Komparator ein, der eine Ausnahme auslöst, wenn er versucht, null zu vergleichen, und er spuckt einen neuen Vergleicher aus, der genauso funktioniert, außer dass er Null-Argumente zulässt. Deshalb brauchen Sie einen Parameter zu nullsFirst - weil es einen neuen Komparator oben auf einem vorhandenen Komparator baut, und Sie sagen ihm, was der vorhandene Komparator ist.

Also warum gibt es Ihnen nicht die natürliche Reihenfolge, wenn Sie den Parameter weglassen? Weil sie es nicht so definiert haben. nullsFirst ist im javadoc definiert einen Parameter zu nehmen:

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

Ich glaube, wenn die Designer wollen, könnten sie eine Überlastung hinzugefügt haben, die keine Parameter:

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

, dass das gleiche sein würde wie mit nullsFirst(naturalOrder()). Aber das taten sie nicht, also kannst du es nicht so benutzen.

+0

Hallo ajb ich dachte nie, dass der einzige Parameter Komparator Handle Null wäre .. ich dachte nur die nullsFirst () Methode würde die Nullen zuerst und die Nicht-Nullen würde auch in einer natürlichen Reihenfolge sortiert werden ... aber wie Sie sagten 'statische Comparator nullsFirst() // Hinweis: nicht legal' einfach ist nicht legal. Ich dachte nur, wäre nett, wie Sie sagten, eine nullFirst ohne Parameter sollte natürlichOrder() als Standard .. danke. – chiperortiz

11

Versuchen:

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

ich mit Namen und ID eine Liste der Mitarbeiter mit dem Student habe ..

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


    } 

} 

Ausgang erzeugt als

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]