浅析Java中Comparable与Comparator接口

欢迎跳转到本文原文链接 Backend_Notes


我们在使用 Arrays.sortCollections.sort 时会发现,调用该方法的类必须实现 Comparable接口,才能保证方法的正确调用,我们先来看Comparable 接口。

Comparable

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

This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class’s natural ordering, and the class’s compareTo method is referred to as its natural comparison method.

Lists (and arrays) of objects that implement this interface can be sorted automatically by Collections.sort (and Arrays.sort). Objects that implement this interface can be used as keys in a sorted map or as elements in a sorted set, without the need to specify a comparator.

This interface imposes a total ordering on the objects of each class that implements it. This ordering is referred to as the class’s natural ordering, and the class’s compareTo method is referred to as its natural comparison method.

Lists (and arrays) of objects that implement this interface can be sorted automatically by Collections.sort (and Arrays.sort). Objects that implement this interface can be used as keys in a sorted map or as elements in a sorted set, without the need to specify a comparator.

实现了 Comparable 接口的类可以进行排序,这种排序称为类的 natural ordering, 该类的 Lists 或 arrays 可以使用Collections.sort()Arrays.sort() 方法进行排序,并且该对象可以作为 sorted map 的键 或 sorted set的元素(不用显示声明 comparator)

代码示例:

public class Student implements Comparable<Student>{
    private String name;
    private double salary;

    public Student(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", salary=" + salary +
                '}';
    }

    @Override
    public int compareTo(Student other) {
        return Double.compare(salary, other.salary);
    }
}

public class StudentSortTest {
    public static void main(String[] args) {
        Student[] students = new Student[3];

        students[0] = new Student("Alice", 2000);
        students[1] = new Student("Bob", 500);
        students[2] = new Student("Cici", 400);

        Arrays.sort(students);

        for (Student student : students) {
            System.out.println(student);
        }
    }
}

// output
// Student{name='Cici', salary=400.0}
// Student{name='Bob', salary=500.0}
// Student{name='Alice', salary=2000.0}

注意:
compareTo 方法在实现是有可能使用两个整数相减来进行比较(o1 - o2), 当所得结果范围过大时可能会造成溢出,因此推荐使用 Integer.compare() 方法, 当比较两个浮点数时,因为存在舍入误差,推荐使用 Double.compare()进行比较。

在这里我们思考一个问题,我们为什么不能直接在 Student 类中实现一个 compareTo方法来实现排序,而是通过实现接口的形式来完成呢?主要原因是 Java 是强类型(Strongly typed)语言,在调用方法时,编译器会检查这个方法是否存在。

Comparator

Comparator 接口也是用来进行排序的,但是当我们的类本身没有实现 Comparable 接口,并且是不能修改(用final修饰, 此时不能通过创建其子类并实现 comparable),又或者该类本身已实现了 Comparable,但想要自定义排序,此时就可以使用 Comparator接口。

public final class Student2 {
    private String name;
    private int age;

    public Student2(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Student2{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

public class StudentSortTest2 {
    public static void main(String[] args) {
        List<Student2> list = new ArrayList<>();

        list.add(new Student2("Alice", 18));
        list.add(new Student2("Bob", 14));
        list.add(new Student2("Cici", 12));

        list.sort(new Comparator<Student2>() {
            @Override
            public int compare(Student2 o1, Student2 o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        
        // lambda
		// list.sort((o1, o2) -> o1.getAge() - o2.getAge());
        
        // method reference:comparingInt
        // list.sort(Comparator.comparingInt(Student2::getAge));

        for (Student2 student2 : list) {
            System.out.println(student2);
        }
    }
}

// output
// Student2{name='Cici', age=12}
// Student2{name='Bob', age=14}
// Student2{name='Alice', age=18}

在《Effective Java》一书中,作者Joshua Bloch推荐大家在编写自定义类的时候尽可能的考虑实现一下Comparable接口,一旦实现了 Comparable 接口,它就可以跟许多泛型算法以及依赖于该接口的集合实现进行协作。你付出很小的努力就可以获得非常强大的功能。

事实上,Java平台类库中的所有值类都实现了Comparable 接口。如果你正在编写一个值类,它具有非常明显的内在排序关系,比如按字母顺序、按数值顺序或者按年代顺序,那你就应该坚决考虑实现这个接口。

compareTo 方法不但允许进行简单的等同性进行比较,而且语序执行顺序比较,除此之外,它与Object的equals方法具有相似的特征,它还是一个泛型。类实现了 Comparable 接口,就表明它的实例具有内在的排序关系,为实现Comparable接口的对象数组进行排序就这么简单: Arrays.sort(a);

对存储在集合中的Comparable对象进行搜索、计算极限值以及自动维护也同样简单。[2]

总结

Comparable 是一个排序接口,值类可以实现其接口来支持排序,相当于内部比较器,与类耦合。

Comparator 是一个比较器接口,可以被各种需要比较功能的类使用,相当与外部比较器,与类解耦。

Reference

  1. Java核心技术卷一
  2. Comparable与Comparator浅析
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值