Java 8 Comparator nullsFirst naturalOrder confused

匿名 (未验证) 提交于 2019-12-03 01:52:01

问题:

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

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 Comparatorcomparator = comparing(Persona::getName,nullsFirst(naturalOrder()));     final Listpersons = 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 OK with me but I have a problem understanding.

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

final Comparatorcomparator = comparing(Persona::getName); 

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

final Comparatorcomparator = comparing(Persona::getName,nullsFirst()); 

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

But the right code is this:

final Comparatorcomparator = comparing(Persona::getName,nullsFirst(naturalOrder())); 

I don't understand the parameter to nullsFirst. I just thought the natural order of name would explicitly [default] even handle null values.

But the docs say:

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

This line: "If both are non-null, the specified Comparator is used to determine the order."

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

回答1:

The "natural order" comparator, which is what you get when you use comparing with only one parameter, does not handle nulls. (I'm not sure where you got the idea that it did.) The "natural order" of a Comparable class is defined by the compareTo() method, which is used like this:

obj1.compareTo(obj2) 

Obviously this won't work if obj1 is null; for String, it will also throw an exception of obj2 is null.

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

The nullsFirst() method (and nullsLast() similarly) basically transforms a Comparator to a new Comparator. You put in a comparator that may throw an exception if it tries to compare null, and it spits out a new comparator that works the same way except that it allows null arguments. So that's why you need a parameter to nullsFirst--because it builds a new comparator on top of an existing comparator, and you tell it what the existing comparator is.

So why doesn't it give you the natural order if you leave out the parameter? Because they didn't define it that way. nullsFirst is defined in the javadoc to take a parameter:

static  Comparator nullsFirst(Comparator super T> comparator) 

I think that if the designers wanted to, they could have added an overload that takes no parameters:

static  Comparator nullsFirst()  // note: not legal 

that would be the same as using nullsFirst(naturalOrder()). But they didn't, so you can't use it like that.



回答2:

Try:

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


回答3:

I have a list of Employee with Student 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 list = new ArrayList();         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);         }       }  } 

Produces output 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] 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!