Java 集合排序策略接口 Comparator

简介:

Java 集合排序策略接口 Comparator

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

List peoples = new ArrayList<>();
// 中间省略
// 按照年龄从小到大排序
peoples.sort(Comparator.comparing(People::getAge));
这里排序用到了一个关键接口 java.util.Comparator。排序比较作为业务中经常出现的需求,我们有必要研究一下这个接口。

  1. Comparator 概念
    Comparator 是一个函数式接口。它经常用于没有天然排序的集合进行排序,如 Collections.sort 或 Arrays.sort。或者对于某些有序数据结构的排序规则进行声明,如 TreeSet 、TreeMap 。也就是该接口主要用来进行集合排序。
  2. Comparator 中的方法
    Comparator 作为一个函数式接口只有一个抽象方法,但是它有很多的默认方法,我们来认识一下这些方法们。

3.1 compare 抽象方法
作为Comparator 唯一的抽象方法,int compare(T o1,T o2) 比较两个参数的大小, 返回负整数,零,正整数 ,分别代表 o1o2,通常分别返回 -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 Comparator 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 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 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。

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

原文地址https://www.cnblogs.com/felordcn/p/12921857.html

相关文章
|
2天前
|
Java 开发者
Java一分钟之-Lambda表达式与函数式接口
【5月更文挑战第12天】Java 8引入的Lambda表达式简化了函数式编程,与函数式接口结合,实现了代码高效编写。本文介绍了Lambda的基本语法,如参数列表、箭头符号和函数体,并展示了如何使用Lambda实现`Runnable`接口。函数式接口仅有一个抽象方法,可与Lambda搭配使用。`@FunctionalInterface`注解用于确保接口具有单一抽象方法。文章还讨论了常见的问题和易错点,如非函数式接口、类型冲突以及Lambda表达式的局部变量可见性,并提供了避免这些问题的策略。通过理解Lambda和函数式接口,开发者能提高代码可读性和效率。
42 4
|
18小时前
|
消息中间件 Java Kafka
Java大文件排序(有手就能学会),kafka面试题2024
Java大文件排序(有手就能学会),kafka面试题2024
|
23小时前
|
存储 Java 编译器
Java中的抽象类与接口,在阿里工作5年了
Java中的抽象类与接口,在阿里工作5年了
|
2天前
|
存储 安全 算法
【常见集合】Java 常见集合重点解析
【常见集合】Java 常见集合重点解析
6 0
|
2天前
|
Java 编译器 开发者
Java并发编程中的锁优化策略
【5月更文挑战第15天】 在Java的多线程编程中,锁机制是实现线程同步的关键。然而,不当的锁使用往往导致性能瓶颈甚至死锁。本文深入探讨了Java并发编程中针对锁的优化策略,包括锁粗化、锁消除、锁分离以及读写锁的应用。通过具体实例和性能分析,我们将展示如何有效避免竞争条件,减少锁开销,并提升应用程序的整体性能。
|
2天前
|
Java API 容器
Java8函数式编程接口:Consumer、Supplier、Function、Predicate
Java8函数式编程接口:Consumer、Supplier、Function、Predicate
8 1
|
2天前
|
存储 算法 搜索推荐
【Java高阶数据结构】图补充-拓扑排序
【Java高阶数据结构】图补充-拓扑排序
7 1
|
2天前
|
Java ice
【Java开发指南 | 第二十九篇】Java接口
【Java开发指南 | 第二十九篇】Java接口
9 0
|
2天前
|
Java
【Java开发指南 | 第九篇】访问实例变量和方法、继承、接口
【Java开发指南 | 第九篇】访问实例变量和方法、继承、接口
14 4
|
2天前
|
Java 编译器 开发者
Java并发编程中的锁优化策略
【5月更文挑战第13天】在Java并发编程中,锁是一种重要的同步机制,用于保证多线程环境下数据的一致性。然而,不当的使用锁可能会导致性能下降,甚至产生死锁等问题。本文将介绍Java中锁的优化策略,包括锁粗化、锁消除、锁降级等,帮助开发者提高程序的性能。