Java中Comparable和Comparator的使用

Java为使用者提供了两种比较机制:Comparable和Comparator,它们的名字虽然很像,但是实际使用上却并不相同。

总的来说:Comparable是一个排序接口,Comparator是一个比较器接口。接下来会详细介绍二者的区别。

一、Comparable

Comparable是一个排序接口,一个类如果Implements了Comparable接口,那么就代表着这个类是“可比较的”或者说“可排序的”。更具体点来说就是,如果你的类实现了这个接口,那么你可以直接调用Collections.sort()方法或者Arrays.sort()方法将一个容器里的类对象排序。(不过这样排序默认是自然排序,即升序排列)

下面我们来看看Java内部的Comparable到底是怎样的:

package java.lang;
import java.util.*;
public interface Comparable {
    public int compareTo(T o);
}

从上面的代码可以看出,Comparable是一个接口,并且它使用了泛型。接口内部只有一个方法compareTo。

任何实现Comparable接口的类都必须重写这个compareTo方法。compareTo方法的目的是比较该对象和指定对象o之间的大小。若该对象大于o则返回正整数,小于o则返回负整数,相等则返回0。(由这个返回值的分布你可以猜想到,它的实现方法可以是将两个对象相减,然后返回相减的结果)

以一个Person类为例:

public class Person implements Comparable{
    public String name;
    public Integer age;

    public Person(String name,Integer age){
        this.name=name;
        this.age=age;
    }

    @Override
    public int compareTo(Person o) {
        return this.age-o.age;
    }
}

我们希望Person类是可排序的,所以让它实现了Comparable接口,所以在Person类内部重新的compareTo方法,实现方法就是返回二者的age之差。

public class ComparableDemo {
    public static void main(String[] args) {
        Person p1 = new Person("张三", 18);
        Person p2 = new Person("张四", 20);
        Person p3 = new Person("张五", 5);
        List people = new ArrayList<>();
        people.add(p1);people.add(p2);people.add(p3);
        System.out.println("排序前:");
        people.forEach(person -> System.out.println(person.name + person.age + "岁"));
        Collections.sort(people);
        System.out.println("排序后:");
        people.forEach(person -> System.out.println(person.name + person.age + "岁"));
    }
}

Java中Comparable和Comparator的使用_第1张图片

 运行结果如上图所示,调用Collections.sort()方法后people链表被按照升序排列了。 

另外,实现了 Comparable 接口的对象才能够直接被用作 SortedMap (SortedSet) 的 key。

public static void main(String[] args) {
        Person p1 = new Person("张三", 18);
        Person p2 = new Person("张四", 20);
        Person p3 = new Person("张五", 5);
        Map map=new TreeMap<>();
        map.put(p1,100.0);map.put(p2,70.2);map.put(p3,88.3);
        map.forEach((person, aDouble) -> 
            System.out.println(person.name+person.age+"岁,得分为:"+aDouble));
    }

Java中Comparable和Comparator的使用_第2张图片

 

这里的sortedmap用TreeMap来实现。 可以看到结果是按照年龄的升序排列了。

Comparable小结:Comparable接口适用于这个类是可排序的,而且默认为升序。你当然可以在重写compareTo的时候修改比较逻辑,这样可以实现降序排列。但是并不建议这样做,因为当代码量变大之后,我们如果没记住compareTo的结果是反的的话,在其他地方调用compareTo很可能会得到与我们预期相反的结果。

二、Comparator

Comparator是比较接口,我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过实现Comparator来新建一个比较器,然后通过这个比较器对类进行排序。该接口定义如下:

package java.util;
public interface Comparator{
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

可以看到,Comparator也使用了泛型,它的内部用两个方法,一个是compare一个是equals。

注意:在实现Comparator接口的时候,必须要重写compare方法,但是可以不重写equals方法。

compare方法是用来描述比较逻辑的,它与Comparable中的compareTo方法类似。

一般我们都在使用sort函数的时候new一个比较器来描述我们想要的排序逻辑,但是请先记住一点,sort方法一定是按照compare的结果升序排列的。

compare(T o1,T o2)意味着”比较o1与o2"。如果我想把compare定义为:若o1大于o2则返回正整数,若o1小于o2则返回负整数,若o1等于o2则返回0。那么sort函数会按照这个compare的结果升序排列。

public static void main(String[] args) {
        Person p1 = new Person("张三", 18);
        Person p2 = new Person("张四", 20);
        Person p3 = new Person("张五", 5);
        List people = new ArrayList<>();
        System.out.println("排序前:");
        people.add(p1);people.add(p2);people.add(p3);
        people.forEach(person -> System.out.println(person.name + person.age + "岁"));

        people.sort(new Comparator() {
            @Override
            public int compare(Person o1, Person o2) {
                if(o1.age>o2.age) return 1;
                if(o1.age System.out.println(person.name+person.age+"岁"));
    }

Java中Comparable和Comparator的使用_第3张图片

 可以看到按照上面的排序逻辑,调用sort方法排序,结果是升序排列的。(这和Comparable相同)

但是我们如果将comparator的比较逻辑反过来:

people.sort(new Comparator() {
            @Override
            public int compare(Person o1, Person o2) {
                if(o1.age>o2.age) return -1;
                if(o1.age

Java中Comparable和Comparator的使用_第4张图片

 可以看到,反过来之后就变成了降序排列。所以,当我们在不同场景下需要不同的排序逻辑时,我们只需要修改Comparator的排序逻辑即可。

这里也可以总结一点关于写Comparator排序逻辑的经验方法:当我们希望按照某个属性自然排序的时候,那么就按照正常比大小的结果返回正负整数以及0即可。如果想要按照某个属性降序排列的时候,我们可以这样看:o1要排在o2前面,所以返回负整数,而我们希望的是降序,那么必然o1大于o2,所以在if语句里填上o1>o2的条件即可。

三、总结

Java 中的两种排序方式:

  1. Comparable 自然排序。(实体类实现)
  2. Comparator 是定制排序。(无法修改实体类时,直接在调用方创建)

对于一些普通的数据类型(比如 String, Integer, Double…),它们默认实现了Comparable 接口,实现了 compareTo 方法,我们可以直接使用。

而对于一些自定义类,如果它的比较逻辑比较简单,并且排序不需要更改逻辑,那么可以实现Comparable。但是当我们的排序逻辑很复杂而且可能会变动的时候,更方便的方法就是创建 Comparator比较器,然后按照你期望的排序逻辑实现compare方法。

两种方法各有优劣, 用Comparable 简单, 只要实现Comparable 接口的对象直接就成为一个可以比较的对象,但是需要修改源代码。 用Comparator 的好处是不需要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去就可以比大小了, 并且在Comparator 里面用户可以自己实现复杂的可以通用的逻辑,使其可以匹配一些比较简单的对象,那样就可以节省很多重复劳动了。

你可能感兴趣的:(java,开发语言)