13-Java中Comparable和Comparator的区别?

下面分别对Comparable 和 Comparator做具体介绍并总结。

Comparable

Comparable可以认为是一个内比较器,实现了Comparable接口的类有一个特点,就是这些类是可以和自己比较的,至于具体和另一个实现了Comparable接口的类如何比较,则依赖compareTo方法的实现。

如果add进入一个Collection的对象想要Collections的sort方法帮你自动进行排序的话,那么这个对象必须实现Comparable接口。compareTo方法的返回值是int,有三种情况:

  • 比较者大于被比较者,返回正整数

  • 比较者等于被比较者,返回0

  • 比较者小于被比较者,返回负整数

写个很简单的例子:

/**
 * 实现Comparable重写compareTo方法进行Person的排序, 这里根据年龄排序
 * 
 * @author 
 * @date 
 */
@Getter
@Setter
@ToString
@AllArgsConstructor
@NoArgsConstructor
public class Person implements Comparable<Person> {

    private String name;
    private int age;

    @Override
    public int compareTo(Person p) {
        // return p.getAge() - this.getAge()
        return this.getAge() - p.getAge();
    }
}
public class Demo {

    public static void main(String[] args) {

        Person p1 = new Person("aaa", 11);
        Person p2 = new Person("bbb", 5);
        Person p3 = new Person("cc", 8);

        Person[] ps = {p1, p2, p3};
        System.out.println(Arrays.toString(ps)); // 排序前
        Arrays.sort(ps);
        System.out.println(Arrays.toString(ps)); // 排序后

        System.out.println("----------------");

        Map<Person, String> map = new TreeMap<>();
        map.put(p1, "aaa");
        map.put(p2, "bbb");
        map.put(p3, "ccc");
        System.out.println(map.toString());

        System.out.println("----------------");

        List<Person> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        Collections.sort(list); // 排序
        System.out.println(list.toString());
    }
}

执行结果:

[Person(name=aaa, age=11), Person(name=bbb, age=5), Person(name=cc, age=8)]
[Person(name=bbb, age=5), Person(name=cc, age=8), Person(name=aaa, age=11)]
----------------
{Person(name=bbb, age=5)=bbb, Person(name=cc, age=8)=ccc, Person(name=aaa, age=11)=aaa}
----------------
[Person(name=bbb, age=5), Person(name=cc, age=8), Person(name=aaa, age=11)]

Comparator

Comparator接口里面有一个compare方法,方法有两个参数T o1和T o2,是泛型的表示方式,分别表示待比较的两个对象,方法返回值和Comparable接口一样是int,有三种情况:

  • o1大于o2,返回正整数

  • o1等于o2,返回0

  • o1小于o3,返回负整数

写个很简单的例子:

public class ComparatorDemo {
    
    public static void main(String[] args) {
        /**
         * 按照数组元素长度排序
         */
        String[] str = {"abcdeftg", "maidi", "kebi", "zhanmusi"};
        
        Arrays.sort(str, new Comparator<String>() {
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });
        System.out.println(Arrays.toString(str));
}

这里的Arrays.sort对数组进行排序用到了Compartor接口, 上面使用了匿名函数的写法, 也可以使用lambda进行改造...

Arrays.sort(str, (o1, o2) -> o1.length() - o2.length());

一行代码就可以搞定, 的确很简单

运行结果

[kebi, maidi, abcdeftg, zhanmusi]

因为泛型指定死了,所以实现Comparator接口的实现类只能是两个相同的对象(不能一个自定义对象、一个String)进行比较,实现Comparator接口的实现类一般都会以"待比较的实体类+Comparator"来命名, 只不过这里使用了匿名函数更简单而已

总结

上面的例子不管是Comparable还是Comparator, 都是按照升序来排序的, 如果想要按照反过来的顺序, 改改代码即可, 只需要修改接口中的实现方法, 调换一下相减的两个比较对象或者值即可, 例如"// return p.getAge() - this.getAge()"注释掉的代码那里一样

如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,写比较算法。

实现Comparable接口的方式比实现Comparator接口的耦合性要强一些,如果要修改比较算法,要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。因此:

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

  • 而对于一些自定义类,它们可能在不同情况下需要实现不同的比较策略,我们可以新创建 Comparator 接口,然后使用特定的 Comparator 实现进行比较。

不同之处:

实现Comparable接口的方式比实现Comparator接口的耦合性要强