Java中Comparable与Comparator的区别

java,comparable,comparator,区别 · 浏览次数 : 3

小编点评

**Comparable 接口** * 位于 java.lang 包下 * 实现 Comparable 接口时,需要实现 compareTo 方法 * compareTo 方法返回值决定了我们的排序顺序: - 返回负值(-1 或更小):当前对象排目标对象前面,即正序排列 - 返回 0:当前对象和目标对象相等,排序不分先后返回正值(1 或 更大):当前对象排目标对象后面,即倒序排列 **Comparator 接口** * 位于 java.util 包下 * 实现 Comparator 接口时,需要实现 compare 方法 * compare 方法接收 2 个待比较的对象 o1、o2,并假设返回值为负值(-1 或更小)代表 o1 排 o2 前面,反之 o1 排 o2 后面 * 返回值为正值(1 或 更大):对象 A 也一定是大于对象 C 的 **总结** * Comparable 是 Java 中类排序接口,用于实现类的内部比较规则 * Comparator 是 Java 中比较器接口,用于实现对象的比较规则 * Comparable 接口的 compareTo 方法用于比较对象之间的排序,而 Comparator 接口的 compare 方法用于根据具体业务要求进行排序

正文

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

Comparable 接口

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

package java.lang;

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

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

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

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

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());
    }
}

我们来测试下:

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 是怎么实现的:

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 包下,定义如下:

@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 从小到大排序的功能:

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 排序的话:

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 的方式。

与Java中Comparable与Comparator的区别相似的内容:

Java中Comparable与Comparator的区别

Java 中的 Comparable 和 Comparator 都是比较有用的集合排序接口,但是这俩接口使用却有着明显区别,具体使用哪一个接口,今天我们来一起了解下。 Comparable 接口 Comparable 是一个排序接口,位于 java.lang 包下面,实现该接口的类就可以进行自然排序

Java中可以用的大数据推荐算法

在Java中实现大数据推荐算法时,通常会使用一些开源的机器学习库,如Apache Mahout、Weka、DL4J(DeepLearning4j,用于深度学习)或者Spark MLlib(用于在Spark集群上运行)。由于完整实现一个大数据推荐算法的代码量可能非常大,并且需要配合具体的数据集和环境进

java中SimpleDateFormat解析日期格式的问题

在日常写代码的过程中,我们经常要处理各种格式的日期,常见的日期格式有:“20240601”,“2024-06-01”,“2024-6-1”。如何正确地处理日期格式,尤其是对外接口中参数的日期格式,就很重要了,一个不小心就可能出现意想不到的问题。 举一个我遇到的真实例子:我们提供的对外接口中有一个参数

java中判断String类型为空和null的方法

1.判断一个String类型的变量是否为空(即长度为0)或者为null 在Java中,判断一个String类型的变量是否为空(即长度为0)或者为null,通常需要使用两个条件语句来进行检查。这是因为null表示变量没有引用任何对象,而空字符串("")表示变量引用了一个没有内容的字符串对象。 下面是一

Java遍历Map集合的方法

Java中遍历Map集合的常用方式主要有以下几种: 1.使用keySet()方法遍历 遍历Map的key集合,然后通过key获取value。 Map map = new HashMap<>(); map.put("one", 1); map.put("two", 2

京东二面:Sychronized的锁升级过程是怎样的

Java中Synchronized锁升级通过偏向锁、轻量级锁到重量级锁的动态转变,优化了多线程同步性能。偏向锁减少无竞争场景的开销,轻量级锁借助CAS与自旋优化低竞争环境,重量级锁确保高竞争下的互斥性。合理设计并发模型,监控锁状态并结合其他并发工具以充分利用锁升级优势。

Java 中的深拷贝和浅拷贝你了解吗?

Java 开发中,对象拷贝是常有的事,很多人可能搞不清到底是拷贝了引用还是拷贝了对象。本文将详细介绍相关知识,让你充分理解 Java 拷贝。

Java中CAS算法的集中体现:Atomic原子类库,你了解吗?

一、写在开头 在前面的博文中我们学习了volatile关键字,知道了它可以保证有序性和可见性,但无法保障原子性,结局原子性问题推荐使用synchronized、Lock或者AtomicInteger;我们还学习过CAS算法,在那篇博文中我们同样也提及atomic。那么今天,我们就来好好学一学Atom

字节面试:说说Java中的锁机制?

Java 中的锁(Locking)机制主要是为了解决多线程环境下,对共享资源并发访问时的同步和互斥控制,以确保共享资源的安全访问。 锁的作用主要体现在以下几个方面: 互斥访问:确保在任何时刻,只有一个线程能够访问特定的资源或执行特定的代码段。这防止了多个线程同时修改同一资源导致的数据不一致问题。 内

Java中的读写锁ReentrantReadWriteLock详解,存在一个小缺陷

写在开头 最近是和java.util.concurrent.locks包下的同步类干上了,素有 并发根基 之称的concurrent包中全是精品,今天我们继续哈,今天学习的主题要由一个大厂常问的Java面试题开始: 小伙子,来说一说Java中的读写锁,你都用过哪些读写锁吧? 这个问题小伙伴们遇到了该