【equals 、Comparable、Comparator 三者的区别】

作者: 小飞学编程..._CSDN博客-编程小白

专栏:JavaEE初阶

标题:equals 、Comparable、Comparator 三者的区别

equals 、Comparable、Comparator 三者的区别

引言

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 对象

相关推荐
青 春 记 忆1 小时前
零基础入门python21:Flask账本的数据模型——用户、分类和账目
python·flask·后端开发
骇客野人1 小时前
Java分布式任务调度方案(完整落地指南)
java·开发语言·分布式
hy.z_7771 小时前
【软件测试】8. 自动化测试实践
python
JacksonMx1 小时前
Spring @Async 坑点 + 改造方案
java·开发语言
楠楠子呀1 小时前
从零构建高性能 WhatsApp 链接生成器与重定向服务:架构与路由解析
redis·python·容器·架构·flink·散列表·flume
Dovis(誓平步青云)1 小时前
牛奶不能减成负数:冰箱库存的筛选、去重与不可变更新
android·java·开发语言
OPEN-F2 小时前
Python进阶教程:算法与数据结构入门
开发语言·python
薛定猫AI2 小时前
【技术干货】Claude Code多模型代理与反馈闭环:Python实现可验证的AI编程工作流
开发语言·python·ai编程