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

Java常用类-比较器

目录

  • 一、为什么需要比较器?
  • 二、核心差异速记表
  • 三、Comparable:对象自带的 “默认规则”
    • 1. 核心作用
    • 2. 源码定义
    • 3. 实战:给Student类加默认规则
    • 4. 源码验证(以Integer为例)
  • 四、Comparator:临时的 “外部规则”
    • 1. 核心作用
    • 2. 源码定义
    • 3. 实战:给 Student 加临时规则
      • 方式 1:匿名内部类(传统写法)
      • 方式 2:Lambda 表达式(Java 8+ 简化写法)
    • 4. 源码验证(以 Arrays.sort 为例)
      • 4.1 Arrays.sort()
        • 4.1.1 无Comparator时
        • 4.1.2 有 Comparator 时
      • 4.2 TreeMap中的比较器优先级
  • 五、Comparator 的进阶
    • 1. 多条件排序(先按 A,再按 B)
    • 2. 空值处理(允许 null)
    • 3. 反转顺序
  • 六、最佳实践总结
    • 1.​​比较器三原则​​(违反会导致排序异常):
    • 2.​​整数比较的陷阱​​
    • 3.选择策略​​:
    • 4.JDK8+高效写法​​:
  • 七、记忆口诀(一句话记住核心)
    • 1.Comparable:
    • 2.Comparator:
    • 3.优先级:
  • 八、常见面试题
    • 1.Comparable 和 Comparator 的区别?
    • 2.如果一个类没有实现 Comparable,能否排序?
    • 3.compareTo 和 compare 方法的返回值有什么要求?
    • 4.如何对自定义对象排序?
    • 5.Comparator 有哪些常用方法?
    • 6.如何实现逆序排序?​​
    • 7.​​比较器在HashMap中的作用?​​

一、为什么需要比较器?

场景:

  • 对 Integer、String 等内置类型排序时,JDK 知道怎么比大小(如 1 < 2,“a” < “b”)。
  • 但对自定义对象(如 User、Product),JDK 不知道按什么规则排序(按年龄?按价格?)。
    比较器就是用来定义 “对象比较规则” 的工具。

二、核心差异速记表

Comparable(自然排序)Comparator(定制排序)
​​包位置​​java.langjava.util
​​接口方法​​compareTo(T o)compare(T o1, T o2)
​​使用场景​​类本身的默认排序规则临时定义多种排序策略
规则位置写在类内部(实现接口)写在类外部(单独定义)
​​修改代码​​需要修改类源码不修改原有类
​​排序方式​​单一自然排序支持多种排序规则
​​典型应用​​String、Integer等包装类的排序第三方库排序,多条件排序

三、Comparable:对象自带的 “默认规则”

1. 核心作用

让对象 “自带” 一个比较规则,就像人天生知道 “年龄大的更年长” 一样。只要对象实现了Comparable,就能直接用Arrays.sort() 或Collections.sort() 排序。

2. 源码定义

// JDK 源码(java.lang.Comparable)
public interface Comparable<T> {int compareTo(T o); // 定义比较规则的方法
}
  • 返回值含义(重点!):
    • 如果 this < o → 返回负数(比如 -1)。
    • 如果 this == o → 返回 0。
    • 如果 this > o → 返回正数(比如 1)。

3. 实战:给Student类加默认规则

假设Student默认按分数升序排序:

public class Student implements Comparable<Student> {private String name;private int score;// 重写 compareTo:定义“分数升序”规则@Overridepublic int compareTo(Student other) {return this.score - other.score; // 分数小的排前面}// 构造方法、getter 省略...
}

使用示例:

Student[] students = {new Student("张三", 85),new Student("李四", 75),new Student("王五", 90)
};Arrays.sort(students); // 直接排序!因为 Student 实现了 Comparable
// 排序后顺序:李四(75)、张三(85)、王五(90)

4. 源码验证(以Integer为例)

Integer能直接排序,因为它实现了Comparable:

// JDK 源码(java.lang.Integer)
public final class Integer implements Comparable<Integer> {public int compareTo(Integer another) {return this.value - another.value; // 按数值大小比较}
}

四、Comparator:临时的 “外部规则”

1. 核心作用

当对象没有实现Comparable,或者需要临时改变排序规则(比如Student平时按分数排,但今天要按姓名排),就用Comparator。

2. 源码定义

// JDK 源码(java.util.Comparator)
@FunctionalInterface // 函数式接口(可 Lambda)
public interface Comparator<T> {int compare(T o1, T o2); // 定义比较规则的方法
}
  • 返回值含义和 Comparable 一致:
    • o1 < o2 → 负数;o1 == o2 → 0;o1 > o2 → 正数。**

3. 实战:给 Student 加临时规则

需求:Student平时按分数排(Comparable),但今天需要按姓名长度降序排。

方式 1:匿名内部类(传统写法)

// 定义一个“姓名长度降序”的比较器
Comparator<Student> nameLengthComparator = new Comparator<Student>() {@Overridepublic int compare(Student s1, Student s2) {// 姓名长度大的排前面(降序)return s2.getName().length() - s1.getName().length();}
};// 使用这个比较器排序
Arrays.sort(students, nameLengthComparator); 
// 排序后顺序:张三(2字)、李四(2字)、王五(2字)→ 若长度相同,保持原顺序

方式 2:Lambda 表达式(Java 8+ 简化写法)

// 用 Lambda 简化 Comparator 定义
Comparator<Student> nameLengthComparator = (s1, s2) -> s2.getName().length() - s1.getName().length();Arrays.sort(students, nameLengthComparator); // 效果同上

4. 源码验证(以 Arrays.sort 为例)

4.1 Arrays.sort()

// 在TimSort(Java排序算法实现)中的关键代码
if (c.compare(a[runHi++], a[lo]) < 0) { // 使用比较器判断顺序// 执行元素交换等操作
}

Arrays.sort 有两种重载,分别对应 Comparable 和 Comparator:

4.1.1 无Comparator时
public static <T extends Comparable<? super T>> void sort(T[] a) {// 直接调用对象的compareTo方法TimSort.sort(a, 0, a.length, null, 0, 0);
}

约束:数组元素必须实现Comparable,否则报ClassCastException。

4.1.2 有 Comparator 时
public static <T> void sort(T[] a, Comparator<? super T> c) {// 使用传入的ComparatorTimSort.sort(a, 0, a.length, c, 0, 0);
}

灵活性:无需元素实现Comparable,临时传入规则即可。

4.2 TreeMap中的比较器优先级

public TreeMap(Comparator<? super K> comparator) {this.comparator = comparator; // 比较器优先于自然排序
}final int compare(Object k1, Object k2) {return comparator==null ? ((Comparable)k1).compareTo(k2): comparator.compare(k1, k2);
}

五、Comparator 的进阶

1. 多条件排序(先按 A,再按 B)

需求:学生先按分数降序,分数相同则按姓名升序。

// 组合比较器:先按分数降序,分数相同按年龄升序
Comparator<Student> complexComparator = Comparator.comparingInt(Student::getScore).reversed() // 分数降序(先主条件).thenComparingInt(Student::getAge);// 分数相同,按姓名升序(次条件)Arrays.sort(students, multiComparator);// 相当于:
(s1, s2) -> {int scoreCompare = Integer.compare(s2.getScore(), s1.getScore());return (scoreCompare != 0) ? scoreCompare : Integer.compare(s1.getAge(), s2.getAge());
};

2. 空值处理(允许 null)

// null 排在最前面(nullsFirst)
Comparator<Student> nullsFirstComparator = Comparator.nullsFirst(Comparator.comparing(Student::getScore));// null 排在最后面(nullsLast)
Comparator<Student> nullsLastComparator = Comparator.nullsLast(Comparator.comparing(Student::getScore));

3. 反转顺序

Comparator<Student> scoreAsc = Comparator.comparingInt(Student::getScore); // 分数升序
Comparator<Student> scoreDesc = scoreAsc.reversed(); // 反转成降序

六、最佳实践总结

1.​​比较器三原则​​(违反会导致排序异常):

自反性:compare(a, a) == 0
对称性:compare(a, b) == -compare(b, a)
传递性:若compare(a, b) > 0且compare(b, c) > 0,则compare(a, c) > 0

2.​​整数比较的陷阱​​

// 错误写法(可能溢出):
return o1.id - o2.id;// 正确写法:
return Integer.compare(o1.id, o2.id);

3.选择策略​​:

  • 类有自然顺序 → 实现Comparable
  • 需要多种排序方式 → 使用Comparator
  • 第三方类排序 → 必须用Comparator

4.JDK8+高效写法​​:

// 多字段排序
users.sort(Comparator.comparing(User::getLastName).thenComparing(User::getFirstName));// 按字符串长度排序
Comparator.comparing(String::length);

七、记忆口诀(一句话记住核心)

1.Comparable:

  • 类内定义 “默认规则”,
  • 所有排序都用它(如学生默认按分数);
  • 例子:User implements Comparable,重写 compareTo。

2.Comparator:

  • 类外定义 “临时规则”,
  • 哪里需要哪里传(如学生临时按姓名);
  • 例子:Collections.sort(users, (u1, u2) -> …)。

3.优先级:

当Comparable和Comparator同时存在时,Comparator优先(覆盖默认规则)。

八、常见面试题

1.Comparable 和 Comparator 的区别?

答:Comparable 是类内部实现的接口(compareTo),定义对象的默认排序规则;Comparator 是外部定义的接口(compare),用于临时修改排序规则。

2.如果一个类没有实现 Comparable,能否排序?

答:可以!通过 Comparator 传入临时规则(如 Arrays.sort(数组, 自定义Comparator))。

3.compareTo 和 compare 方法的返回值有什么要求?

答:必须满足:负数(前者小)、0(相等)、正数(前者大)。如果返回值逻辑错误,排序会乱。

4.如何对自定义对象排序?

答:两种方式:
① 让类实现Comparable接口,重写compareTo。
② 不修改类,创建Comparator并传给排序方法。

5.Comparator 有哪些常用方法?

答:reversed()(反转)、thenComparing()(多条件排序)、nullsFirst()/nullsLast()(空值处理)。

6.如何实现逆序排序?​​

// 方法1:反转比较结果
Comparator<Student> reversed = (s1, s2) -> s2.compareTo(s1);// 方法2:使用内置方法
Comparator.comparing(Student::getScore).reversed();

7.​​比较器在HashMap中的作用?​​

HashMap不依赖比较器,但TreeMap的排序依赖比较器

相关文章:

  • 华为云Flexus+DeepSeek征文|DeepSeek-V3/R1商用服务开通教程以及模型体验
  • [Java实战]Spring Boot 静态资源配置(十三)
  • ARM GIC(七)亲和路由:GICD_IROUTER寄存器具体如何与MPIDR配合使用?
  • 2050年的世界是怎样的?
  • tensorflow 1.x
  • AI智慧公园管理方案:用科技重塑市民的“夜游体验”
  • kubectl系列(十二):查询pod的resource 配置
  • C++编程中,什么是野指针?
  • Linux系统编程之消息队列
  • 关于TIAV20 PLCSIM仿真错误的原因
  • ST表(稀疏表)
  • Unity基础学习(十一)核心系统---光源系统基础
  • 机器人运动控制原理浅析-UC Berkeley超视觉模态模型
  • 【Python 算法零基础 2.模拟 ⑤ 基于栈和队列】
  • 【递归、搜索与回溯算法】导论
  • java加强 -List集合
  • 应急响应基础模拟靶机-security2
  • 御网杯2025 Web,Msic,密码 WP
  • 深入解析多选字段的存储与查询:从位运算到数据库设计的最佳实践
  • uni-app学习笔记(二)--vue页面代码的构成和新建页面
  • 王毅谈中拉命运共同体建设“五大工程”及落实举措
  • 中国巴西民间推动建立经第三方验证的“森林友好型”牛肉供应链
  • 习近平同巴西总统卢拉会谈
  • 国内首例侵入式脑机接口系统前瞻性临床试验:受试者已能用意念玩游戏
  • 复旦相辉堂上演原创历史人物剧《王淑贞》,胡歌参演
  • 著名军旅作家、文艺评论家周政保逝世,享年77岁