web-dev-qa-db-fra.com

Java 8 Comparateur nullsFirst naturalOrder confused

cela peut être une question simple mais je voudrais bien la comprendre ...

J'ai un code comme celui-ci:

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

Et je teste ce code:

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

Cela montre les résultats suivants:

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}

Ces résultats me conviennent, mais j'ai un problème de compréhension.

Lorsque j'ignore l'objet new Persona(5,null) et que je passe le comparateur:

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

Il fonctionne comme un charme. Mon tri se fait par natural order of name property. Le problème se pose lorsque j'ajoute l'objet avec name=null, Je pensais juste que j'aurais besoin de mon comparateur comme celui-ci.

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

Ma pensée était erronée: "OK, quand le nom n'est pas nul, ils sont triés dans natural order of name, Tout comme le comparateur précédent, et s'ils sont null ils sera le premier mais mes noms non nuls seront toujours triés dans l'ordre naturel ".

Mais le bon code est le suivant:

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

Je ne comprends pas le paramètre de nullsFirst. Je pensais juste que le natural order of name Gérerait explicitement [par défaut] même les valeurs de null.

Mais les documents disent:

Renvoie un comparateur convivial null qui considère que null est inférieur à non nul. Lorsque les deux sont null, ils sont considérés comme égaux. Si les deux ne sont pas nuls, le Comparator spécifié est utilisé pour déterminer l'ordre. Si le comparateur spécifié est null, le comparateur renvoyé considère que toutes les valeurs non nulles sont égales.

Cette ligne: "Si les deux ne sont pas nuls, le Comparator spécifié est utilisé pour déterminer l'ordre."

Je suis confus quand et comment l'ordre naturel doit être explicitement défini ou quand ils sont déduits.

14
chiperortiz

Le comparateur "ordre naturel", qui est ce que vous obtenez lorsque vous utilisez comparing avec un seul paramètre, ne gère pas les valeurs NULL. (Je ne sais pas d'où vous est venue l'idée.) "L'ordre naturel" d'une classe Comparable est défini par la méthode compareTo(), qui est utilisée comme ceci:

obj1.compareTo(obj2)

Évidemment, cela ne fonctionnera pas si obj1 Est nul; pour String, il lèvera également une exception si obj2 est nul.

La méthode naturalOrder() renvoie un Comparator qui compare deux objets. javadoc indique explicitement que ce comparateur jette NullPointerException lors de la comparaison de null.

La méthode nullsFirst() (et nullsLast() de la même manière) transforme fondamentalement un Comparator en un nouveau Comparator. Vous mettez dans un comparateur qui peut lever une exception s'il essaie de comparer null, et il crache un nouveau comparateur qui fonctionne de la même manière, sauf qu'il autorise les arguments null. C'est pourquoi vous avez besoin d'un paramètre pour nullsFirst-- car il crée un nouveau comparateur au-dessus d'un comparateur existant, et vous lui dites ce qu'est le comparateur existant.

Alors, pourquoi ne vous donne-t-il pas l'ordre naturel si vous omettez le paramètre? Parce qu'ils ne l'ont pas défini de cette façon. nullsFirst est défini dans javadoc pour prendre un paramètre:

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

Je pense que si les concepteurs l'avaient voulu, ils auraient pu ajouter une surcharge qui ne prend aucun paramètre:

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

ce serait la même chose que d'utiliser nullsFirst(naturalOrder()). Mais ils ne l'ont pas fait, vous ne pouvez donc pas l'utiliser comme ça.

27
ajb

Essayer:

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

J'ai une liste d'employé avec étudiant avec nom et identifiant ..

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


    }

}

Produit une sortie en tant que

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]
4
Nikhil Kumar K