Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
720 views
in Technique[技术] by (71.8m points)

string - Java 8 Comparator nullsFirst naturalOrder confused

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 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 OK with me but I have a problem understanding.

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

final Comparator<Persona>comparator = 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 Comparator<Persona>comparator = 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 Comparator<Persona>comparator = 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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...