欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 教育 > 锐评 > Java常用类-比较器

Java常用类-比较器

2025/5/15 5:43:19 来源:https://blog.csdn.net/super_Zhu0818/article/details/147877473  浏览:    关键词: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的排序依赖比较器

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词