java比较器中的自然排序,Java 利用比较器实现排序

Java 利用比较器实现排序,用户希望实现对象的排序,使用传入 Comparator 的 Stream.sorted 方法,Comparator 既可以通过 lambda 表达式实现,也可以使用 Comparator 接口定义的某种 comparing 方法生成。

Java 利用比较器实现排序 问题描述

用户希望实现对象的排序。

Java 利用比较器实现排序 解决方案

使用传入 Comparator 的 Stream.sorted 方法,Comparator 既可以通过 lambda 表达式实现,也可以使用 Comparator 接口定义的某种 comparing 方法生成。

Java 利用比较器实现排序 具体实例

Stream.sorted 方法根据类的自然顺序(natural ordering)生成一个新的排序流,自然顺序是通过实现 java.util.Comparable 接口来指定的。

如例 4-1 所示,我们对字符串集合进行排序。

例 4-1 根据字典序(自然顺序)对字符串排序

❶ 默认排序(Java 7 及之前的版本)

❷ 默认排序(Java 8 及之后的版本)

从 Java 1.2 引入集合框架(collections framework)开始,工具类 Collections 就已存在。Collections 类定义的静态方法 sort 传入 List 作为参数,但返回 void。这种排序是破坏性的,会修改所提供的集合。换言之,Collections.sort 方法不符合 Java 8 所倡导的将不可变性(immutability)置于首要位置的函数式编程原则。

Java 8 采用 Stream.sorted 方法实现相同的排序,但不对原始集合进行修改,而是生成一个新的流。在例 8-1 中,完成集合的排序后,程序根据类的自然顺序对返回列表进行排序。对于字符串,自然顺序是字典序(lexicographic order);如果所有字符串均为小写,自然顺序就相当于字母顺序(alphabetical order),从本例可以观察到这一点。

如果希望以其他方式排序字符串,可以使用 sorted 方法的重载形式,传入 Comparator 作为参数。

例 4-2 采用两种不同的方式,根据长度对字符串进行排序。

例 4-2 根据长度对字符串排序

❶ 使用 lambda 表达式作为 Comparator,根据长度进行排序

❷ 使用 Comparator.comparingInt 方法

sorted 方法的参数为 java.util.Comparator,它是一种函数式接口。对于第一个方法 lengthSortUsingSorted,所提供的 lambda 表达式用于实现 Comparator.compare 方法。在 Java 7 及之前的版本中,实现通常由匿名内部类提供,而本例仅需要一个 lambda 表达式。

Java 8 引入 sort(Comparator) 作为 List 接口的默认实例方法,它相当于 Collections 类的 sort(List, Comparator) 方法。由于两种方法返回 void 且都是破坏性的,本节讨论的 Stream.sorted(Comparator) 方法(返回一个新的排序流)仍然是首选方案。

第二个方法 lengthSortUsingComparator 利用了 Comparator 接口新增的某种静态方法。comparingInt 方法传入一个 ToIntFunction 类型的参数(文档称之为 keyExtractor),用于将字符串转换为整型数据,并生成一个使用该 keyExtractor 对集合进行排序的 Comparator。

Comparator 接口新增的各种默认方法极为有用。虽然不难写出一个根据长度进行排序的 Comparator,但如果需要对多个字段排序,代码可能会变得很复杂。例如,我们希望根据长度对字符串排序,如果长度相同则按字母顺序排序。如例 4-3 所示,采用 Comparator 接口提供的默认和静态方法很容易就能解决这个问题。

例 4-3 根据长度对字符串排序,长度相同则按字母顺序排序

➊ 根据长度对字符串排序,长度相同则按字母顺序排序

Comparator 接口定义了一个称为 thenComparing 的默认方法。与 comparing 方法类似,thenComparing 方法也传入 Function 作为参数,文档同样将其称为 keyExtractor。如果将 thenComparing 方法链接到 comparing 方法会返回 Comparator,它首先比较第一个数量,如果相同则比较第二个数量,以此类推。

静态导入通常使代码更易于阅读。一旦熟悉 Comparator 接口和 Collectors 类定义的各种静态方法,就能写出更简单的代码。在本例中,comparing 和 naturalOrder 方法已被静态导入。

即便没有实现 Comparable 接口,上述方案也适用于任何类。如例 4-4 所示,我们定义一个描述高尔夫球手的 Golfer 类。

例 4-4 描述高尔夫球手的 Golfer 类

为了创建一个高尔夫锦标赛排行榜,可以依次根据各个球手的得分、姓氏、名字进行排序。例 4-5 显示了如何实现高尔夫球手的排序。

例 4-5 对高尔夫球手排序

调用 sortByScoreThenLastThenFirst 方法,输出如例 4-6 所示。

例 4-6 对高尔夫球手排序后的结果

本例首先根据得分对各个球手进行排序,因此 Jack Nicklaus 和 Ty Webb1 排在 Tiger Woods、Bubba Watson 与 Tom Watson 之前。得分相同时根据姓氏进行排序,因此 Jack Nicklaus 排在 Ty Webb 之前,Bubba Watson 和 Tom Watson 排在 Tiger Woods 之前。如果得分和姓氏都相同,则根据名字进行排序,因此 Bubba Watson 排在 Tom Watson 之前。

1当然,Ty Webb 是电影《小小球童》中的角色。Judge Smails 问道:“今天打得怎么样?”Ty Webb 回答:“我没有计分。”Smails 继续问道:“那你怎么衡量自己与其他球手的水平呢?”Webb 回答:“身高。”根据身高进行排序留给读者作为简单的练习。

借由 Comparator 接口定义的默认和静态方法以及 Stream 接口新增的 sorted 方法,生成复杂的排序变得易如反掌。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值