Java 8 Comparator nullsFirst naturalOrder is confused

This may be a simple question, but I would like to clearly understand it ...

I have a code like this:

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

And I am testing this 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); } 

This shows the following results:

 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} 

These results are okay with me, but I have a problem with understanding.

When I ignore the new Persona(5,null) object and pass the comparator:

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

It works like a charm. My sorting is natural order of name property . The problem arises when I add an object with name=null , I just thought that I needed my comparator like this.

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

My thought was wrong : "OK, when the name is not null, they are sorted in the natural order of name , like the previous comparator, and if they are null , they will be the first , but my non-zero names will still be sorted in natural order .

But the correct code is this:

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

I do not understand the nullsFirst parameter. I just thought that the natural order of name explicitly [by default] even handles null values.

But the docs say:

Returns a null-friendly comparator that considers null less than null. When both are null , they are considered equal. If both values ​​are not equal to zero, the specified Comparator to determine the order. If the specified comparator is null , then the returned comparator considers all non-zero values ​​equal.

This line: "If both values ​​are non-zero, the specified Comparator used to determine the order.

I am confused when and how the natural order should be explicitly established or when they are deduced.

+10
source share
3 answers

The "natural order" comparator that you get when you use comparing with only one parameter does not handle zeros. (I'm not sure where you got the idea that this happened.) The "natural order" of the Comparable class Comparable determined by the compareTo() method, which is used as follows:

 obj1.compareTo(obj2) 

Obviously this will not work if obj1 is null; for String it will also throw an exception if obj2 is null.

The naturalOrder() method returns a Comparator which compares two objects. Javadoc explicitly says that this comparator throws a NullPointerException when comparing null.

The nullsFirst() method (and nullsLast() similarly) basically converts Comparator to the new Comparator . You insert a comparator that can throw an exception if it tries to compare zero, and it throws a new comparator that works the same way, except that it accepts null arguments. Therefore, for what you need the nullsFirst --because parameter, it builds a new comparator on top of the existing comparator, and you tell it what the existing comparator is.

So why doesn’t it give you a natural order if you skip the parameter? Because they didn’t define it that way. nullsFirst defined in javadoc to accept a parameter:

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

I think that if designers wanted, they could add an overload that takes no parameters:

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

this will be the same as using nullsFirst(naturalOrder()) . But they did not, so you cannot use it like that.

+22
source

Try:

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

I have an Employee with Student list with name and 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); } } } 

Outputs as

 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] 
+2
source

Source: https://habr.com/ru/post/1204606/


All Articles