Java 8 Comparator nullsFirst naturalOrder confused
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 if 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 <T> Comparator<T> nullsFirst(Comparator<? super T> comparator)
I think that if the designers wanted to, they could have added an overload that takes no parameters:
static <T> Comparator<T> 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.
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<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);
}
}
}
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]
Try:
final Comparator<Persona> comparator =
comparing(Persona::getName, nullsFirst(naturalOrder()));