首页 > 编程语言 >Java中Comparable与Comparator的区别

Java中Comparable与Comparator的区别

时间:2023-09-12 22:47:03浏览次数:42  
标签:Comparable Java Comparator age userList User compareTo

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

标签:Comparable,Java,Comparator,age,userList,User,compareTo
From: https://www.cnblogs.com/vipzhou/p/17698041.html

相关文章

  • Java学习_005 if语句:奇偶数的判定
    需求:任意给出一个整数,使用程序判定该整数是奇数还是偶数,并在控制台输出。1importjava.util.Scanner;23publicclassMain{4publicstaticvoidmain(String[]args){5Scannersc=newScanner(System.in);6System.out.println("please......
  • java继承
    继承与合成基本概念继承:可以基于已经存在的类构造一个新类。继承已经存在的类就可以复用这些类的方法和域。在此基础上,可以添加新的方法和域,从而扩充了类的功能。合成:在新类里创建原有的对象称为合成。这种方式可以重复利用现有的代码而不更改它的形式。1.继承的语法关键字e......
  • Java反序列化:CommonsCollections3调试分析
    基础知识1.Java反射1.1getConstructorgetConstructor是Java反射API中的一个方法,用于获取类的公共构造方法的引用。构造方法是一种特殊的方法,用于创建类的实例(对象),并且通常在对象创建时进行初始化。getConstructor的函数原型:publicConstructor<?>getConstructor(Class......
  • Error:java: 错误: 不支持发行版本 5(17)
    调试IDEA时出现如下错误, 参考:https://blog.csdn.net/xiao_yi_xiao/article/details/119142118 出现原因:本地配置jdk和idea默认的jdk不匹配JDK环境13 Module对应5 ......
  • Java 接收参数的7种方式
    1.直接在Controller方法参数上配置参数名@RequestMapping("/method01")publicStringmethod01(Stringname,Integerage,Doublemon){Useruser=newUser();user.setName(name);user.setAge(age);user.setMoney(mon);r......
  • 深入理解 @PostConstruct 注解及其在 Java 开发中的应用
    深入理解@PostConstruct注解及其在Java开发中的应用本文将深入探讨Java中的@PostConstruct注解,介绍其作用、使用方法和常见应用场景。我们将详细解释注解的生命周期和执行顺序,以及如何正确使用@PostConstruct注解来进行初始化操作。通过本文的学习,读者将对@PostConstru......
  • Java入门
    java入手注释pvsmJava入口点程序逻辑中枢sout输出//单行注释/**/多行注释/**回车文档注释标识符注意事项标识符不能含关键字标识符需大小写字母开头或$与_开头标识符后可接大小写字母与$_和数字的任意组合标识符大小写敏感八大基本数据类型整数类型字节......
  • Java入门
    java入手注释pvsmJava入口点程序逻辑中枢sout输出//单行注释/**/多行注释/**回车文档注释标识符注意事项标识符不能含关键字标识符需大小写字母开头或$与_开头标识符后可接大小写字母与$_和数字的任意组合标识符大小写敏感八大基本数据类型整数类型字节......
  • JavaScript深拷贝的具体实现方法解析
    什么是深拷贝?深拷贝是指创建一个新对象或数组,使其与原始对象或数组具有相同的值,但是两者是完全独立的,互不影响。深拷贝不仅复制了对象或数组本身,还递归复制了其所有嵌套的对象和数组,确保所有层级的数据都是独立的。实现深拷贝的方法在JavaScript中,实现深拷贝的方法有很多种,下面将介......
  • 无涯教程-JavaScript - TBILLPRICE函数
    描述TBILLPRICE函数返回面值$100的国库券的价格。语法TBILLPRICE(settlement,maturity,discount)争论Argument描述Required/OptionalSettlement国库券的结算日期。证券结算日期是发行国库券给买方的发行日期之后的日期。RequiredMaturity国库券的到期日。......