当前位置: 首页 > news >正文

Comparator接口与Lambda表达式

引言

在你学习过Lambda表达式和java的函数引用之后,你就可以进一步了解Comparator这个接口了,它提供了很多有用的方法,以及很多函数式接口的参数供你使用,本质的目的是为了让你可以尽可能简单的编写一个排序方法。

注:本文末尾提供完整的代码。

不使用Lambda表达式的排序方法

假设我们有一些Person对象,每个对象都有一个String的名字,而我们希望对这个Person数组或者集合按照姓名的长度进行排序,我们会想到什么方法呢?

我们会很自然的想到让Person对象继承Comparable接口,并重写compare方法,使得比较两个Person对象的方式是根据姓名长度:

class Person implements Comparable<Person> {private String name;public Person(String name) {this.name = name;}@Overridepublic int compareTo(Person o) {return this.name.length() - o.name.length();}
}

但是这会有一个问题,就是如果我们今后对Person所在的数组或者集合希望有多种排序方式的时候,这种方式就无法实现多种排序方式。

因此基础扎实的同学就可能会想到,我们可以自定义比较器,然后通过传递不同的自定义比较器对象去实现不同的排序方式:

public class LambdaTest {public static void main(String[] args){Person[] list = new Person[3];list[0] = new Person("Zhu","Ya","Xuan");list[1] = new Person("Yi","Wen","Nian");list[2] = new Person("Fu","Xiao","Yu");// 手动创建比较器并使用Arrays.sort(list, new PersonLengthComparator());}
}class PersonLengthComparator implements Comparator<Person> {@Overridepublic int compare(Person o1, Person o2) {return Integer.compare(o1.getName().length(),o2.getName().length());}
}

但是我们会发现,这实在是太麻烦了,每次都要单独写一个辅助类去完成一个不同功能的排序策略。

那么有没有一种方法,可以让我们避免写这么多复杂的代码呢?

为简洁而生Lambda

lambda是一个可传递的代码块,可以在以后执行一次或者多次。

java会自动根据lambda表达式生成一个目标所需的对象,传递给其他代码使用。

Arrays.sort(list, (a,b) -> a.getName().length() - b.getName().length());

上述这个例子我们可以放心大胆的这样去理解:我们为sort方法提供了一个比较器,这个比较器比较两个对象时,根据第一个对象的name长度减去第二个对象的name长度确认compare比较的结果值,系统在执行时根据这个比较值的结果确认它们的大小。

怎么样,是不是感觉这样的代码语言就跟人类的语言很相似了,这就是lambda的强大之处。

当然,系统的Comparator的还为我们提供了一些有用的方法,可以让我们对上面这个式子的内容做的更好:

Arrays.sort(list, Comparator.comparingInt(a -> a.getName().length()));

Comparator接口的一些有用方法

具体的代码已经放在了下方的完整代码中,以下仅简述特性:

1.可以对comparing方法提取的键,再提供一个比较器。

2.使用.thenComparing可以提供多级比较。

3.可以提供一个Comparator.nullsFirst适配器,它会修改现有的比较器。

注:使用nullsFirst时,一定要为这个方法内提供一个修饰比较器参数,如果除了对null值处理的要求以外没有其他要求了,也要提供一个Comparator.naturalOrder()占位。

4.有一个提供反向比较器的静态方法Comparator.reverseOrder()。

5.由于我们只是要提供一个Comparator比较器对象,所以上述内容对数组和集合同样适用。

6.排序操作如果对一个不可变集合使用,将会抛出UnsupportedOperationException异常

注:该异常通常意味着你在尝试修改一个不可修改的集合。(详情见下方代码)

import javax.swing.Timer;
import java.util.*;public class LambdaTest {public static void main(String[] args){Person[] list = new Person[3];list[0] = new Person("Zhu","Ya","Xuan");list[1] = new Person("Yi","Wen","Nian");list[2] = new Person("Fu","Xiao","Yu");// 手动创建比较器并使用Arrays.sort(list, new PersonLengthComparator());// 使用lambda表达式省略自定义Comparator比较器的过程Arrays.sort(list, (a,b) -> a.getName().length() - b.getName().length());Arrays.sort(list, Comparator.comparingInt(a -> a.getName().length()));// 可以对comparing方法提取的键,再提供一个比较器Arrays.sort(list, Comparator.comparing(Person::getName, (o1, o2) -> o1.length() - o2.length()));Arrays.sort(list, Comparator.comparing(Person::getName, Comparator.comparingInt(String::length)));// 可以使用comparingInt方法配合lambda表达式优化上面的语句,同上Arrays.sort(list, Comparator.comparingInt(p -> p.getName().length()));// 使用.thenComparing可以提供多级比较Arrays.sort(list, Comparator.comparingInt((Person p) -> p.getFirstName().length()).thenComparingInt(p -> p.getMiddleName().length()));// Comparator.nullsFirst是一个适配器,它会修改现有的比较器// (你也可以认为comparing方法的第二个参数都是这样的适配器)// 如果键函数可能为空(此处为Person::getMiddleName)// 就可以提供nullsFirst适配器,使得做比较的第一个对象为null时也可以正常比较// Comparator.naturalOrder()表示正常的比较(使用nullsFirst时该参数不可省略)Arrays.sort(list, Comparator.comparing(Person::getMiddleName,Comparator.nullsFirst(Comparator.naturalOrder())));// 你自然也可以在里面添加一个普通的比较器Arrays.sort(list, Comparator.comparing(Person::getMiddleName,Comparator.nullsFirst(Comparator.comparingInt(String::length))));// 还有一个提供反向比较器的静态方法Comparator.reverseOrder()Arrays.sort(list, Comparator.comparing(Person::getMiddleName,Comparator.reverseOrder()));// 由于我们只是要提供一个Comparator比较器对象,所以上述内容对数组和集合同样适用List<Person> personList = new ArrayList<>(Arrays.asList(list));personList.sort(Comparator.comparing(Person::getMiddleName, Comparator.reverseOrder()));// 要特别注意,以下操作在上述排序中会抛出 UnsupportedOperationException 异常// List<Person> personList = Arrays.stream(list).toList();// 该异常通常意味着你在尝试修改一个不可修改的集合// 出现该异常的原因是toList这种带to开头的方法返回的是一个不可修改的视图System.out.println(Arrays.toString(list));// lambda表达式的另一个运用var timer = new Timer(1000, e -> System.out.println("每间隔1秒输出1行"));timer.start();while(true){}}
}class PersonLengthComparator implements Comparator<Person> {@Overridepublic int compare(Person o1, Person o2) {return Integer.compare(o1.getName().length(),o2.getName().length());}
}class Person implements Comparable<Person> {public Person(String firstName, String middleName, String lastName) {this.firstName = firstName;this.middleName = middleName;this.lastName = lastName;this.name = firstName + middleName + lastName;}private String firstName;private String middleName;private String lastName;private String name;@Overridepublic String toString() {return name;}public String getFirstName() {return firstName;}public void setFirstName(String firstName) {this.firstName = firstName;}public String getMiddleName() {return middleName;}public void setMiddleName(String middleName) {this.middleName = middleName;}public String getLastName() {return lastName;}public void setLastName(String lastName) {this.lastName = lastName;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic int compareTo(Person o) {return this.name.length() - o.name.length();}
}

相关文章:

  • 回调函数——qsort的模拟实现
  • 『MySQL快速上手』-③-库的操作
  • 一、Hadoop初始化配置(final+ubuntu保姆级教程)
  • 钉钉内嵌H5遇到的一些问题
  • html中使用JQ自定义锚点偏移量
  • 价钱统计
  • 数字摄影测量
  • 换服还是掀桌?哪条才是程序员的出路?
  • C++ [继承]
  • 【delphi】中 TNetHTTPClient 注意事项
  • springboot(ssm 高校教师电子名片系统 Java(codeLW)
  • 【ARM Trace32(劳特巴赫) 使用介绍 2 - Veloce 环境中使用trace32 连接 Cortex-M33】
  • C++ http协议POST body raw 字段向服务器发送请求
  • Linux Vim批量注释和自定义注释
  • flink的CoProcessFunction使用示例
  • 【翻译】babel对TC39装饰器草案的实现
  • Android组件 - 收藏集 - 掘金
  • co模块的前端实现
  • HashMap ConcurrentHashMap
  • Js基础知识(四) - js运行原理与机制
  • Promise面试题2实现异步串行执行
  • windows下使用nginx调试简介
  • 不上全站https的网站你们就等着被恶心死吧
  • 服务器之间,相同帐号,实现免密钥登录
  • 简单基于spring的redis配置(单机和集群模式)
  • 京东美团研发面经
  • 开放才能进步!Angular和Wijmo一起走过的日子
  • 前端工程化(Gulp、Webpack)-webpack
  • 微信开源mars源码分析1—上层samples分析
  • 小程序开发之路(一)
  • 写代码的正确姿势
  • 一个6年java程序员的工作感悟,写给还在迷茫的你
  • AI又要和人类“对打”,Deepmind宣布《星战Ⅱ》即将开始 ...
  • JavaScript 新语法详解:Class 的私有属性与私有方法 ...
  • 格斗健身潮牌24KiCK获近千万Pre-A轮融资,用户留存高达9个月 ...
  • ​html.parser --- 简单的 HTML 和 XHTML 解析器​
  • ​LeetCode解法汇总518. 零钱兑换 II
  • (¥1011)-(一千零一拾一元整)输出
  • (1) caustics\
  • (1)(1.11) SiK Radio v2(一)
  • (10)工业界推荐系统-小红书推荐场景及内部实践【排序模型的特征】
  • (3)nginx 配置(nginx.conf)
  • (4) PIVOT 和 UPIVOT 的使用
  • (JS基础)String 类型
  • (PHP)设置修改 Apache 文件根目录 (Document Root)(转帖)
  • (zt)基于Facebook和Flash平台的应用架构解析
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (规划)24届春招和25届暑假实习路线准备规划
  • (接口封装)
  • (五)大数据实战——使用模板虚拟机实现hadoop集群虚拟机克隆及网络相关配置
  • (循环依赖问题)学习spring的第九天
  • (终章)[图像识别]13.OpenCV案例 自定义训练集分类器物体检测
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .bat批处理(四):路径相关%cd%和%~dp0的区别
  • .NET版Word处理控件Aspose.words功能演示:在ASP.NET MVC中创建MS Word编辑器