Java 集合排序规则接口 Comparator_函数式接口

1. 前言

最近用到了集合排序(基于 Java 8)。现在我能用 ​Stream​ 的就用 ​Stream​ ,真香!排序可以这么写:

List<People> peoples = new ArrayList<>();
// 中间省略
// 按照年龄从小到大排序
peoples.sort(Comparator.comparing(People::getAge));

这里排序用到了一个关键接口 ​​java.util.Comparator​​。排序比较作为业务中经常出现的需求,我们有必要研究一下这个接口。

2. Comparator 概念

​Comparator​​​ 是一个函数式接口。它经常用于没有天然排序的集合进行排序,如 ​​Collections.sort​​​ 或 ​​Arrays.sort​​​或者对于某些有序数据结构的排序规则进行声明,如 ​​TreeSet​​​ 、​​TreeMap​​ 。也就是该接口主要用来进行集合排序。

3. Comparator 中的方法

​Comparator​​ 作为一个函数式接口只有一个抽象方法,但是它有很多的默认方法,我们来认识一下这些方法们。

3.1 compare 抽象方法

作为​​Comparator​​​ 唯一的抽象方法,​​int compare(T o1,T o2)​​​ 比较两个参数的大小, 返回负整数,零,正整数 ,分别代表 ​​o1<o2​​​、​​o1=o2​​​、​​o1>o2​​​,通常分别返回 ​​-1​​​、​​0​​​ 或 ​​1​​。伪表达式:

// 输入两个同类型的对象 ,输出一个比较结果的int数字
(x1,x2)-> int

实现该方法一定要注意以下事项:


  • 必须保证​​compare(x,y)​​ 和​​compare(y,x)​​ 的值的和必须为 ​0​ 。
  • 必须保证比较的顺序关系是可传递的,如果​​compare(x,y)>0​​ 而且​​compare(y,z)>0​​ 则 ​​compare(x,z)>0​​。
  • 如果存在 ​​compare(x,y)=0​​,则对于 ​​z​​ 而言,存在 ​​compare(x, z)==compare(y, z)​​。


然而并不 严格要求​​(compare(x, y)==0) == (x.equals(y))​​。一般说来,任何违背这个条件的 Comparator 实现都应该明确指出这一事实情况。


3.2 comparing 系列方法

从 ​Java 8​ 开始,​​Comparator​​​ 提供了一系列的静态方法,并通过函数式的风格赋予 ​​Comparator​​​ 更加强大和方便的功能,我们暂且称它们为 ​​comparing​​系列方法。

public static <T, U> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(keyComparator);
return (Comparator<T> & Serializable)
(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
keyExtractor.apply(c2));
}

该方法是该系列方法的基本方法。是不是看上去很难懂的样子?我们来分析一下该方法。它一共两个参数都是函数式接口。

第一个参数 ​​Function<? super T, ? extends U> keyExtractor​​​ 表示输入一个是 ​​T​​​ 类型对象,输出一个 ​​U​​​ 类型的对象,举个例子,输入一个 ​​People​​​ 对象返回其年龄 ​​Integer​​ 数值:

//   people -> people.getAge(); 转换为下面方法引用
Function<People, Integer> getAge = People::getAge;

第二个参数 ​​keyComparator​​就很好理解了,表示使用的比较规则。

对 ​​c1​​​,​​c2​​​ 按照 第一个参数 ​​keyExtractor​​​ 提供的规则进行提取特征,然后第二个参数​​keyComparator​​对这两个特征进行比较。下面的式子其实可以概括为 ​3.1​ 的 ​​(x1,x2)-> int​

(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
keyExtractor.apply(c2))


Comparator& Serializable 为 Java 8 新特性:同时满足这两个类型约束


理解了这个方法后,其它该系列的方法就好理解了,这里不再赘述。目前 ​​comparing​​ 系列方法使用更加广泛。我们举一些例子:

List<People> peoples = new ArrayList<>();
// ………………
// 按照年龄从低到高排序
peoples.sort(Comparator.comparing(People::getAge));
// 按照年龄从高到低排序
peoples.sort(Comparator.comparing(People::getAge, (x, y) -> -x.compareTo(y)));


同样你可以使用 ​​java.util.Collections​​​ 或者 ​​Stream​​​ 提供的排序方法来使用​​Comparator​​。


4. 小结

今天对 ​​Comparator​​​进行了简单的分析,它用于构建集合排序的规则,在日常开发中非常有用。下一篇 我们将对另一个和它十分相似的接口 ​​Comparable​​ 进行分析,敬请关注


Java 集合排序规则接口 Comparator_抽象方法_02