Comparator.comparing 对于 Comparator 有什么用

Vis*_*dar 5 java collections lambda comparator

据我了解 Comparator 是一个函数接口,用于比较 2 个对象,int compare(T o1, T o2)作为带有两个参数的抽象函数。但还有一种函数Comparator.comparing(s->s)可以采用只有一个输入参数的 lambda 函数。例如使用流对集合进行排序

        List<String> projects=Arrays.asList("abc","def","sss","aaa","bbb");
        projects.stream().sorted((x,y)->y.compareTo(x)).forEach(s->System.out.println(s));
        projects.stream().sorted(Comparator.comparing(s->s)).forEach(s->System.out.println(s));
Run Code Online (Sandbox Code Playgroud)

Sorted 方法采用 Comparator 作为参数。所以我能够理解第一个 lambda 表达式,但我想知道Comparator.comparing(s->s)ie 的使用是Comparator.comparing()用于将单参数 lambda 表达式转换为双参数表达式,还是还有其他用途。另请解释以下函数声明的部分。

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
Run Code Online (Sandbox Code Playgroud)

Swe*_*per 7

用于Comparator.comparing()将单参数 lambda 表达式转换为双参数?

是的,你可以这样想。

在对事物进行排序时,您应该指定“给定两个事物ab,其中哪个更大,或者它们相等?” 用一个Comparator<T>。anda就是b为什么它有 2 个 lambda 参数,并且您返回一个整数来指示您对该问题的答案。

然而,更方便的方法是指定“给定一个事物,您想要按其x一部分进行排序?”。这就是你可以用 的论证来做的事情。xkeyExtractorComparator.comparing

比较:

/*
given two people, a and b, the comparison result between a and b is the 
comparison result between a's name and b's name
*/
Comparator<Person> personNameComparator = 
    (a, b) -> a.getName().compareTo(b.getName());

/*
given a person x, compare their name
*/
Comparator<Person> personNameComparator = 
    Comparator.comparing(x -> x.getName()); // or Person::getName
Run Code Online (Sandbox Code Playgroud)

后者显然更加简洁和直观。我们倾向于考虑按什么事物排序,而不是如何准确地比较两个事物,以及根据比较结果返回确切的数字。

至于声明comparing

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
Run Code Online (Sandbox Code Playgroud)

<T, U extends Comparable<? super U>>部分首先声明两个泛型类型参数 -T是比较器比较的内容(Person在上面的情况下),并且U是您实际比较的类型(String在上面的情况下),因此它extends Comparable

keyExtractor是您传入的参数,例如,它应该回答“当给定 a 时,您想要比较的a 是什么?”x -> x.getName()的问题。TU

? super如果您对和感到困惑? extends,请阅读什么是 PECS?

如果您还没有意识到,其实现comparing基本上可以归结为:

return (a, b) -> keyExtractor.apply(a).compareTo(keyExtractor.apply(b));
Run Code Online (Sandbox Code Playgroud)


hfo*_*nez 6

Comparator#compare(T o1, T o2)比较两个对象并根据以下条件返回一个整数值:

  • 如果 o1 < o2,则为负值
  • 如果 o1 > o2,则为正值
  • 如果它们相等则为零。

Comparator.comparing(Function<? super T, ? extends U> key)返回一个Comparator<T>按该排序键进行比较的值。

主要区别在于该compare方法提供单点比较,而comparing链接到其他函数以提供多个比较点。

假设你有一堂课Person

public class Person implements Comparable<Person> {
    private String firstName;
    private String lastName;
    private int age;
    // rest of class omitted
}
Run Code Online (Sandbox Code Playgroud)

如果您比较两个Person实例p1p2using compare(p1, p2),则将执行比较,并且将根据类规定的某些自然顺序对两个对象进行排序。相反,如果您想使用 比较相同的两个实例comparing(),则将根据您选择基于类的某些属性进行比较的任何标准来执行比较。例如:Comparator.comparing(Person::getFirstName)

正如我之前所说,因为comparing返回的是一个Comparator而不是一个值,所以您可以链接多个比较。例如:Comparator.comparing(Person::getLastName).thenComparing(Person::getFirstName);

至于返回类型的含义,可以在这里<T, U extends Comparable<? super U>> Comparator<T>找到解释。

我想补充一点,班级必须具有可比性才能compare(T o1, T o2)发挥作用。字符串对象具有可比性,因为它们实现了此接口。也就是说,如果一个类不是Comparable,您仍然可以使用comparing方法,因为正如我所说,您可以选择要使用该类的哪个属性进行比较,并且这些属性可能是可比较的(即在这种情况下为 String上例中的人名或年龄)。