Java 8 Comparator nullsFirst naturalOrder 混淆

2022-09-02 05:10:14

这可能是一个简单的问题,但我想清楚地理解它...

我有一个这样的代码:

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

我正在测试这个代码:

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

这将显示以下结果:

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}

这些结果对我来说是可以的,但我有一个问题理解。

当我忽略对象并传递比较器时:new Persona(5,null)

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

它就像一个魅力。我的排序是 .当我添加对象时,问题就出现了,我只是认为我需要这样的比较器。natural order of name propertyname=null

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

我的想法是错误的:“好吧,当名称为非空时,它们被排序为,就像之前的比较器一样,如果它们是第一个,但我的非空名称仍将按自然顺序排序”。natural order of namenull

但正确的代码是这样的:

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

我不明白参数到。我只是认为会显式[默认]甚至处理值。nullsFirstnatural order of namenull

但是文档说:

返回一个对 null 友好的比较器,该比较器认为小于非 null。当两者都是,它们被认为是相等的。如果两者都不为 null,则使用指定的值来确定顺序。如果指定的比较器是 ,则返回的比较器认为所有非空值都相等。nullnullComparatornull

此行:“如果两者都为非 null,则使用指定的用于确定顺序。Comparator

我对何时以及如何明确地设定自然秩序或何时推断它们感到困惑。


答案 1

“自然顺序”比较器(即仅使用一个参数时获得的比较器)处理 null。(我不知道你从哪里得到这个想法。类的“自然顺序”由方法定义,其用法如下:comparingComparablecompareTo()

obj1.compareTo(obj2)

显然,如果 为 null,这将不起作用;对于 ,如果 为 null,它也将引发异常。obj1Stringobj2

该方法返回比较两个对象的 。javadoc 明确指出,此比较器在比较 null 时会抛出。naturalOrder()ComparatorNullPointerException

该方法(以及类似方法)基本上将 a 转换为新的 。您放入一个比较器,如果它尝试比较 null,它可能会引发异常,并且它会吐出一个新的比较器,该比较器的工作方式相同,只是它允许 null 参数。这就是为什么你需要一个参数 --因为它在现有比较器的基础上构建了一个新的比较器,并且你告诉它现有的比较器是什么。nullsFirst()nullsLast()ComparatorComparatornullsFirst

那么,如果你省略参数,为什么它不给你自然的顺序呢?因为他们没有这样定义它。 在 javadoc 中定义为采用参数:nullsFirst

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

我认为,如果设计人员愿意,他们可以添加一个不带参数的重载:

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

这与使用 相同。但是他们没有,所以你不能那样使用它。nullsFirst(naturalOrder())


答案 2

尝试:

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