Java中Comparable与Comparator的区别

Java 中的 Comparable 和 Comparator 都是比较有用的集合排序接口,但是这俩接口使用却有着明显区别,具体使用哪一个接口,今天我们来一起了解下。

Comparable 接口

Comparable 是一个排序接口,位于 java.lang 包下面,实现该接口的类就可以进行自然排序。先看下 Comparable 接口的定义:

java 复制代码
package java.lang;

public interface Comparable<T> {
    int compareTo(T);
}

实现 Comparable 接口时,需要实现 compareTo 方法,其中参数 T 为我们要比较的目标对象,compareTo 方法返回值决定了我们的排序顺序:

  • 返回负值(-1 或更小):当前对象排目标对象前面,即正序排列
  • 返回 0:当前对象和目标对象相等,排序不分先后
  • 返回正值(1 或 更大):当前对象排目标对象后面,即倒序排列

下面来举个例子,首先我们定义一个 User 对象:

java 复制代码
import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class User implements Comparable<User> {
    private String username; // 用户名
    private Integer age; // 年龄

    @Override
    public int compareTo(User nextUser) {
        // 这里我们先直接使用 Integer 自带的 compareTo 进行比较
        return this.age.compareTo(nextUser.getAge());
    }
}

我们来测试下:

java 复制代码
public static void main(String[] args) {
        List<User> userList = new ArrayList<>();
        userList.add(User.builder().age(11).username("张三").build());
        userList.add(User.builder().age(9).username("李四").build());
        Collections.sort(userList);
        log.info("userList: {}", userList); 
        // userList: [User(username=李四, age=9), User(username=张三, age=11)]
    }

在 User 对象我们实现了 compareTo 方法,使用 Integer 自带的 compareTo 进行比较,我们看下 Integer 的 compareTo 是怎么实现的:

java 复制代码
public static int compare(int x, int y) {
    return (x < y) ? -1 : ((x == y) ? 0 : 1);
}

可以看到,当 x 比 y 小时,返回 -1,即 x 排在 y 前面(根据我们的返回值规则,返回 -1 时:当前对象排目标对象前面);当 x > y 时,返回 1,即 x 排后面,总结下就是 小的值排前大的值排后,所以 Integer 自带的 compareTo 其实就是数值按照从小到大排列,即正排序。

所以 this.age.compareTo(nextUser.getAge()) 其实就是根据 User 的 age 大小进行正排序,小的排前面大的排后面。那么假设有一天我们不想根据 age 排序了,我们想换成根据 username 自然排序,怎么办?如果一股脑调整 User 的 compareTo 逻辑怕是不能应对需求的变动吧,这时候就要用到 Java 中的比较器 Comparator 了。

注意:在实现 Java Comparable 接口时,Java 要求实现必须遵循以下传递比较特性:如果对象 A 大于对象 B,对象 B 大于 对象 C,那么对象 A 也一定是大于对象 C 的。

Comparator 接口

Comparator 又被称为 Java 中的比较器,该接口位于 java.util 包下,定义如下:

java 复制代码
@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

Comparator 接口标注 @FunctionalInterface 代表其是一个函数式接口,与 Comparable 不同,Comparator 可以选择性地允许比较空参数,同时保持传递比较特性的要求。

  • compare 方法传入 2 个待比较的对象 o1、o2,假设返回值为负值(-1 或更小)代表 o1 排 o2 前面,反之 o1 排 o2 后面
  • equals 方法传入一个 Object 对象,用于判断待比较的 2 个对象是否是一个对象,该接口不用实现,因为 Object 顶级类已经默认实现了

接下来我们使用 Comparator 实现根据 age 从小到大排序的功能:

java 复制代码
Collections.sort(userList, (o1, o2) -> {
    return o1.getAge().compareTo(o2.getAge());
});
log.info("userList: {}", userList);
// userList: [User(username=李四, age=9), User(username=张三, age=11)]

同样的,我们想根据 username 排序的话:

java 复制代码
Collections.sort(userList, (o1, o2) -> {
    return o1.getUsername().compareTo(o2.getUsername());
});
log.info("userList: {}", userList);
// [User(username=张三, age=11), User(username=李四, age=9)]

可能大家会疑惑为啥 "张三" 还排在 "李四" 前面了,因为汉字 compareTo 时,默认是基于字符串中每个字符的 Unicode 值进行比较的,然后按照字典排序进行排列,字典排序靠前的自然排前面了。

总结

Comparable 和 Comparator 都能实现类排序,但是区别较大,主要有以下几方面:

  • Comparable 是类的内部比较器,而 Comparator 是外部比较器;Comparable 比较只能有一种实现,但是 Comparator 可以根据具体业务要求变换多种比较规则,比较灵活
  • Comparable 的排序规则只适用于该类的对象,而 Comparator 的排序规则可以适用于不同类型的对象
  • 实现 Comparable 接口的类具有默认排序规则,不用使用时再去定义

日常使用的话,推荐自定义 Comparator 的方式。

本文由mdnice多平台发布

相关推荐
AC赳赳老秦1 分钟前
司法公开数据采集应用:OpenClaw 抓取裁判文书公开信息,批量整理同类参考案例
java·大数据·python·数据挖掘·数据分析·php·openclaw
笑小枫7 分钟前
用 Claude Code 推翻重写笑小枫网站
java·人工智能·spring boot·ai编程
霸道流氓气质20 分钟前
ApiPost 中配置自动获取 Token 并调用业务接口完整指南
java·服务器·数据库
独隅41 分钟前
IntelliJ IDEA 接入多种AI大模型插件终极指南(2026.1 企业合规版)
java·人工智能·intellij-idea
还是奇怪1 小时前
Simon Willison 用 DSPy 优化 Datasette Agent 提示词:提示工程正在变成可测试的软件工程
java·开发语言·软件工程
zfoo-framework1 小时前
1.ansible安装 2.虚拟机克隆
java
码上解惑2 小时前
从 Dify 工作流说起:常用节点怎么选、怎样组合?
java·人工智能·ai·agent·dify·智能体·spring ai
青山木3 小时前
Hot 100 --- 岛屿数量
java·数据结构·算法·leetcode·深度优先·广度优先
程序员-珍3 小时前
报错下载android sdk失败
android·java
糖果店的幽灵3 小时前
langgraph分支之 - 动态分支(Dynamic Branch)
java·前端·javascript·人工智能·langgraph