专栏:JavaEE初阶
标题:equals 、Comparable、Comparator 三者的区别
equals 、Comparable、Comparator 三者的区别
- 引言
- equals:判断对象是否相等
- Comparable:内部比较器(类自己会排序)
- Comparator:外部比较器(灵活定制排序)
- 对比图
- 代码案例
-
- [`1. 基础实体类:Student(整合 equals + Comparable)`](#
1. 基础实体类:Student(整合 equals + Comparable)) - [`2. 外部比较器:Comparator(多种排序规则)`](#
2. 外部比较器:Comparator(多种排序规则)) - `3.测试类:运行看效果`
- ==注意==
- [`1. 基础实体类:Student(整合 equals + Comparable)`](#
引言
1.equals:判断两个对象是不是相等 / 同一个
2.Comparable:让类本身自带「和别人比大小」的能力(内部比较器)
3.Comparator:单独做一个裁判工具,灵活定制排序规则(外部比较器)
equals:判断对象是否相等
本质
它是 Java 所有类的老祖宗Object类里的方法,所有对象天生就有。
默认规则
用==比较,比的是两个对象的内存地址------ 也就是判断 "是不是同一个对象"。
为什么要重写
默认规则不符合业务需求。比如你 new 了两个学生对象,学号都是 1001,现实里我们认为是同一个学生,但默认 equals 会返回false(因为是两个不同的对象,地址不一样)
Comparable:内部比较器(类自己会排序)
本质
1.它是java.lang包下的接口,里面只有一个方法:int compareTo(T o)。
2.一个类实现了 Comparable 接口,就代表:这个类的对象「天生就可以排序、比大小」。
3.排序规则写在类的内部,一个类只能有1 种默认排序规则。
返回值规则(记死这个)
当前对象 > 对方 → 返回正数
当前对象 = 对方 → 返回0
当前对象 < 对方 → 返回负数
Comparator:外部比较器(灵活定制排序)
本质
1.它是java.util包下的接口,核心方法是int compare(T o1, T o2)。
2.它是独立的比较器类,不写在原类里面,单独写一个类实现 Comparator。
3.一个原类可以对应无数种排序规则,想怎么比就怎么写比较器。
用法
排序时把比较器对象传进去
对比图

代码案例
1. 基础实体类:Student(整合 equals + Comparable)
这个学生类里,我们同时做了两件事:
1.重写 equals() + hashCode():自定义「学号相同就算同一个学生」的相等规则
2.实现 Comparable 接口:给类加上「默认按学号升序排序」的能力
java
import java.util.Objects;
// 实现 Comparable<Student> → 类本身自带排序能力(内部比较器)
public class Student implements Comparable<Student> {
private String id; // 学号
private String name; // 姓名
private int age; // 年龄
// 构造方法:创建对象时赋值
public Student(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
// getter方法:外部读取私有属性
public String getId() { return id; }
public String getName() { return name; }
public int getAge() { return age; }
// 重写toString:打印对象时能看到具体内容,而不是内存地址
@Override
public String toString() {
return "Student{" +
"学号='" + id + '\'' +
", 姓名='" + name + '\'' +
", 年龄=" + age +
'}';
}
// ==============================================
// 第一部分:重写 equals() + hashCode()
// 作用:自定义「两个对象什么时候算相等」
// ==============================================
@Override
public boolean equals(Object o) {
// 1. 地址相同 → 肯定是同一个对象,直接返回true
if (this == o) return true;
// 2. 对方为空 / 类型不匹配 → 直接不相等
if (o == null || getClass() != o.getClass()) return false;
// 3. 强转类型,比较核心属性:学号相同就认为相等
Student student = (Student) o;
return Objects.equals(id, student.id);
}
// Java强制规范:equals相等的对象,hashCode必须相等
// 用HashMap、HashSet去重时依赖这个,不一起重写会出bug
@Override
public int hashCode() {
return Objects.hash(id);
}
// ==============================================
// 第二部分:实现 Comparable 接口的 compareTo 方法
// 作用:定义类的「默认排序规则」
// ==============================================
@Override
public int compareTo(Student other) {
// 返回值规则(必须记牢):
// 正数 → 当前对象 > 对方
// 0 → 当前对象 = 对方
// 负数 → 当前对象 < 对方
// 这里定义:默认按学号从小到大(升序)
// 字符串自带compareTo方法,按字典序比较
return this.id.compareTo(other.id);
}
}
2. 外部比较器:Comparator(多种排序规则)
Comparator 是独立于 Student 类之外的「裁判」,想加多少种排序规则就写多少个,完全不用改 Student 类的代码。
2.1按年龄升序的比较器
java
import java.util.Comparator;
// 外部比较器1:按年龄 从小到大 排序
public class AgeAscComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
// s1 - s2 → 升序
// s2 - s1 → 降序
return s1.getAge() - s2.getAge();
}
}
2.2按姓名字典序的比较器
java
import java.util.Comparator;
// 外部比较器2:按姓名字典序排序
public class NameComparator implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
return s1.getName().compareTo(s2.getName());
}
}
3.测试类:运行看效果
java
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class TestDemo {
public static void main(String[] args) {
// 准备3个学生,故意打乱顺序
List<Student> students = new ArrayList<>();
students.add(new Student("1003", "张三", 20));
students.add(new Student("1001", "李四", 18));
students.add(new Student("1002", "王五", 19));
// ====================== 测试1:equals 相等判断 ======================
System.out.println("===== 1. equals 相等判断 =====");
Student s1 = new Student("1001", "李四", 18);
Student s2 = new Student("1001", "李四", 18);
System.out.println("== 比内存地址:" + (s1 == s2)); // false,两个不同对象
System.out.println("equals 比学号:" + s1.equals(s2)); // true,学号相同
System.out.println();
//测试结果:
===== 1. equals 相等判断 =====
== 比内存地址:false
equals 比学号:true
// ====================== 测试2:Comparable 默认排序 ======================
System.out.println("===== 2. Comparable 默认排序(按学号升序) =====");
// 不用传额外参数,直接用类自带的排序规则
Collections.sort(students);
for (Student s : students) {
System.out.println(s);
}
System.out.println();
//测试结果
===== 2. Comparable 默认排序(按学号升序) =====
Student{学号='1001', 姓名='李四', 年龄=18}
Student{学号='1002', 姓名='王五', 年龄=19}
Student{学号='1003', 姓名='张三', 年龄=20}
// ====================== 测试3:Comparator 自定义排序 ======================
// 方式1:用我们单独写好的比较器类
System.out.println("===== 3.1 Comparator 按年龄升序 =====");
Collections.sort(students, new AgeAscComparator());
for (Student s : students) {
System.out.println(s);
}
System.out.println();
//测试结果
===== 3.1 Comparator 按年龄升序 =====
Student{学号='1001', 姓名='李四', 年龄=18}
Student{学号='1002', 姓名='王五', 年龄=19}
Student{学号='1003', 姓名='张三', 年龄=20}
// 方式2:匿名内部类(不用单独建类,临时写规则)
System.out.println("===== 3.2 Comparator 按年龄降序(匿名内部类) =====");
Collections.sort(students, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
// o2 - o1 → 降序
return o2.getAge() - o1.getAge();
}
});
for (Student s : students) {
System.out.println(s);
}
System.out.println();
//测试结果
==== 3.2 Comparator 按年龄降序(匿名内部类) =====
Student{学号='1003', 姓名='张三', 年龄=20}
Student{学号='1002', 姓名='王五', 年龄=19}
Student{学号='1001', 姓名='李四', 年龄=18}
// 方式3:Java 8 Lambda 简写(工作中最常用)
System.out.println("===== 3.3 Comparator 按姓名排序(Lambda写法) =====");
Collections.sort(students, (o1, o2) -> o1.getName().compareTo(o2.getName()));
// 还可以再简化(方法引用):
// Collections.sort(students, Comparator.comparing(Student::getName));
for (Student s : students) {
System.out.println(s);
}
}
}
//测试结果
===== 3.3 Comparator 按姓名排序(Lambda写法) =====
Student{学号='1001', 姓名='李四', 年龄=18}
Student{学号='1002', 姓名='王五', 年龄=19}
Student{学号='1003', 姓名='张三', 年龄=20}
注意
java
Collections.sort(集合, 比较器对象);
Collections:集合工具类,java.util包,提供集合静态方法
①:第一个参数 students:要排序的List 集合,会直接修改原集合,排序后集合内元素顺序改变
②:第二个参数 new AgeAscComparator():创建一个比较器实例,告诉 sort 用什么规则比较两个 Student 对象
