Comparator是一个泛型函数式接口T表示待比较对象的类型

@FunctionalInterface
public interface Comparator<T> {
}

本文将主要介绍Comparator作为函数式接口的用法。

理解下面的内容需要以下知识:函数式接口、Lambda表达式、方法引用。


1. 抽象方法

唯一的(不是重写Object方法的)抽象方法: compare

int compare(T o1, T o2)

该方法根据o1, o2的大小关系返回:负数、0、正数

该方法的比较逻辑依赖于具体的Comparator对象,这样的Comparator对象通过Lambda表达式方法引用进行构造。

Comparator<String> comparator = Comparator.comparingInt(x -> x.length());  // 比较String类对象的长度,关于comparingInt下文会介绍,它返回一个Comparator对象
System.out.println(comparator.compare("Tom", "Jerry")); // -1

2. 常用静态/默认方法

① 静态方法comparing

comparing()有两种重载的形式:

// 源码1
// 功能:根据keyExtractor从输入中提取key,然后返回一个使用key类型默认compreTo()对key进行比较的Comparator对象
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
  • 返回值:comparing返回一个Comparator对象,用于比较T类型对象

  • 参数:comparing的参数keyExtractor是一个Function类型,它接收T类型及T的超类型, 返回U类型及U的子类型。keyExtractor的逻辑用Lambda表达式或方法引用实现

    例如,输入String,提取其长度:

    Comparator<String> keyComparator = Comparator.comparing(String::length); // 从传入的String类型对象中提取(即extract) length (即key)
    keyComparator.compare("Tom", "Jerry");
  • 函数体:

    • 首先,确保keyExtractor不是null
    • 然后,返回一个Lambda表达式,该表达式对c1c2提取出来的key进行比较 (用的是c1c2类实现的compareTo()进行比较)
// 源码 2
// 功能: 根据keyExtractor从输入中提取key,然后返回一个根据keyComparator对key进行比较的Comparator对象
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));
}

与上面类似,只不过将key的比较逻辑由compareTo()替换成了自定义的比较逻辑keyComparator

实例:

package Lambda.ComparatorDemo;

import java.util.Arrays;
import java.util.Comparator; class Person {
private String fname;
private String lname; public Person(String fname, String lname) {
this.fname = fname;
this.lname = lname;
} public String getFname() {
return fname;
} public String getLname() {
return lname;
} @Override
public String toString() {
return fname + " " + lname;
}
} public class ComparingDemo {
public static void main(String[] args) {
Person p1 = new Person("Tom", "Kenn");
Person p2 = new Person("Alice", "Zed");
Person[] persons1 = {p1, p2};
Person[] persons2 = persons1.clone(); // 依据First Name排序(用的是String类的compareTo的比较逻辑)
Arrays.sort(persons1, Comparator.comparing(Person::getFname));
System.out.println("Person1 (Sorted by first name): ");
for (Person p : persons1) {
System.out.println(p);
} System.out.println(); // 依据First Name的长度排序
Arrays.sort(persons2, Comparator.comparing(Person::getFname, Comparator.comparingInt(String::length)));
System.out.println("Person2 (Sorted by length of first name):");
for (Person p : persons2) {
System.out.println(p);
}
}
}

② 静态方法comparingXXX()

  • comparingInt()
  • comparingDouble()
  • comparingLong()

comparing是一样的思路,只不过comparingXXX()comparing中的提取出来的key类型固定为了XXX

public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}

③ 默认方法thenComparing()

thenComparing是默认方法,需要对Comparator对象调用,它用于将多个Comparator结合起来实现“链式的比较”:先根据XXX比较,再根据XXX比较,...

// 源码
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
public class thenComparingDemo {
public static void main(String[] args) {
Person p1 = new Person("Alice", "Jessy");
Person p2 = new Person("Alice", "Ann");
Person p3 = new Person("Tom", "Smith"); Person[] persons = {p1, p2, p3}; // 先根据first name比较,再根据last name的长度比较
Comparator<Person> chainedComparator = Comparator.comparing(Person::getFname)
.thenComparing((x, y) -> (x.getLname().length() - y.getLname().length())); // 先根据first name排序,再根据last name的长度排序
Arrays.sort(persons, chainedComparator);
for (Person p : persons) {
System.out.println(p);
}
}
} /*
Alice Ann
Alice Jessy
Tom Smith

参考:

https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#thenComparing-java.util.Comparator

Java Comparator接口学习笔记的更多相关文章

  1. Java多线程技术学习笔记(二)

    目录: 线程间的通信示例 等待唤醒机制 等待唤醒机制的优化 线程间通信经典问题:多生产者多消费者问题 多生产多消费问题的解决 JDK1.5之后的新加锁方式 多生产多消费问题的新解决办法 sleep和w ...

  2. java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

    java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...

  3. java之jvm学习笔记十三(jvm基本结构)

    java之jvm学习笔记十三(jvm基本结构) 这一节,主要来学习jvm的基本结构,也就是概述.说是概述,内容很多,而且概念量也很大,不过关于概念方面,你不用担心,我完全有信心,让概念在你的脑子里变成 ...

  4. 20155327 java第四周学习笔记

    20155327 java第四周学习笔记 五六章知识整理 1子类与父类 父类是接口或者是抽象类,子类必须继承自父类. 2子类的继承性 在Java中,通过关键字extends继承一个已有的类,被继承的类 ...

  5. 《深入理解Java虚拟机》学习笔记

    <深入理解Java虚拟机>学习笔记 一.走近Java JDK(Java Development Kit):包含Java程序设计语言,Java虚拟机,JavaAPI,是用于支持 Java 程 ...

  6. Java:NIO 学习笔记-3

    Java:NIO 学习笔记-3 根据 黑马程序员 的课程 JAVA通信架构I/O模式,做了相应的笔记 3. JAVA NIO 深入剖析 在讲解利用 NIO 实现通信架构之前,我们需要先来了解一下 NI ...

  7. Java:NIO 学习笔记-2

    Java:NIO 学习笔记-2 上一篇 NIO 学习笔记-1 看了 尚硅谷 的相应教程,此处又对比看了 黑马程序员 的课程 JAVA通信架构I/O模式,做了相应的笔记 前言 在 Java 的软件设计开 ...

  8. Java:NIO 学习笔记-1

    Java:NIO 学习笔记-1 说明:本笔记是根据bilibili上 尚硅谷 的课程 NIO视频 而做的笔记 主要内容 Java NIO 简介 Java NIO 与 IO 的主要区别 缓冲区(Buff ...

  9. JAVA GUI编程学习笔记目录

    2014年暑假JAVA GUI编程学习笔记目录 1.JAVA之GUI编程概述 2.JAVA之GUI编程布局 3.JAVA之GUI编程Frame窗口 4.JAVA之GUI编程事件监听机制 5.JAVA之 ...

随机推荐

  1. 在CentOS7上部署 Kubernetes集群

    yum -y install  etcd docker  flannel kubenetes 一般会遇到没有k8s源的问题,先 yum update -y 看是否有效,如果还是没用就创建yum 源,再 ...

  2. CF97B Superset

    嘟嘟嘟cf 嘟嘟嘟luogu 刚开始我看成了对于一个点\(i\),存在一个点\(j\)满足三个条件之一,而不是任意的\(j\).结果自然\(gg\)了,第二个点就\(WA\)了. 也不知怎么来的思路: ...

  3. Hadoop学习之路(六)HDFS基础

    HDFS前言 HDFS:Hadoop Distributed File System ,Hadoop分布式文件系统,主要用来解决海量数据的存储问题 设计思想 1.分散均匀存储 dfs.blocksiz ...

  4. virtualbox+vagrant学习-2(command cli)-26-vagrant share命令

    Share share命令初始化了一个vagrant share会话,允许你与世界上任何一个人共享vagrant环境,允许在几乎任何网络环境中直接在vagrant环境中进行协作. 你可以在本博客的vi ...

  5. Sequelize-nodejs-11-Raw queries

    Raw queries原始查询 就是使用了原始的查询语句,如UPDATE users SET y = 42 WHERE x = 12 As there are often use cases in w ...

  6. mongodb的学习-6-命令简单使用

    1.创建数据库 use DATABASE_NAME 举例说明: > use another //创建了数据库another switched to db another > db anot ...

  7. ddt 接口示范以及报告生成html案例

    1.数据构造获取我就不写了直接以test_data=[  ] 构造一个简单数据建模.实际你从哪里获取根据情况excel也好yaml也罢 2.用例套件处理,报告生成处理 import ddtimport ...

  8. P1474 货币系统 Money Systems

    题目描述 母牛们不但创建了它们自己的政府而且选择了建立了自己的货币系统.由于它们特殊的思考方式,它们对货币的数值感到好奇. 传统地,一个货币系统是由1,5,10,20 或 25,50, 和 100的单 ...

  9. Ural 1183 Brackets Sequence(区间DP+记忆化搜索)

    题目地址:Ural 1183 最终把这题给A了.. .拖拉了好长时间,.. 自己想还是想不出来,正好紫书上有这题. d[i][j]为输入序列从下标i到下标j最少须要加多少括号才干成为合法序列.0< ...

  10. iOS 11 使用方法替换(Method Swizzling),去掉导航栏返回按钮的文字

    方法一:设置BarButtonItem的文本样式为透明颜色,代码如下: [[UIBarButtonItem appearance] setTitleTextAttributes:@{NSForegro ...