Comparable 与 Comparator 比较

1.两个接口的原型

Java中,Comparable与Comparator接口都是用来做比较的。那么这两个接口在实际使用中到底有什么不同呢?下面我们来结合实例分析一下。

先看看两个接口在JDK中的原型。

package java.lang;
import java.util.*;

public interface Comparable<T> {
    public int compareTo(T o);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
package java.util;

import java.io.Serializable;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction;
import java.util.function.ToDoubleFunction;
import java.util.Comparators;

@FunctionalInterface
public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

2.Comparable的用法

一般来说,Comparable是为了对某个类的集合进行排序,所以此时一般都是这个需要排序的类本身去实现Comparable接口。换句话说,如果某个类实现了Comparable接口,那么这个类的数组或者说List就可以进行排序了。

举个简单的例子:

public class Employee implements Comparable<Employee> {

    private String name;
    private int salary;

    public Employee(String name, int salary) {
        this.name = name;
        this.salary = salary;
    }

    @Override
    public int compareTo(Employee other) {
        return this.salary - other.salary;
    }

    @Override
    public String toString() {
        return "name is: " + name + ", salary is: " + salary;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

在客户端中实现Employee集合排序:

public class CompareTest {

    public static List<Employee> genList() {
        Employee e1 = new Employee("aaa",100);
        Employee e2 = new Employee("bbb",150);
        Employee e3 = new Employee("ccc", 80);
        List<Employee> list = new ArrayList();

        list.add(e1);
        list.add(e2);
        list.add(e3);

        return list;
    }

    public static void t1() {
        List<Employee> list = genList();
        //Collections.sort(list); 两种方式都可以,此种方式源码中就是调用的list.sort(null)
        list.sort(null);
        System.out.println(list);
    }

    public static void main(String[] args) {
        t1();
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

将客户端的代码run起来,最后输出的结果为:

[name is: ccc, salary is: 80, name is: aaa, salary is: 100, name is: bbb, salary is: 150]
  • 1

因为Employee实现了Comparable接口,所以能直接对Employee数组进行排序。

3.Comparator接口用法

很多时候我们无法对类进行修改,或者说此类修改的成本太高,但是又希望对其进行排序。那怎么办?这个时候Comparator接口就排上了用场。 
比如我们将前面的Employee类稍作修改,不实现Comparable接口,加上final关键字:

public final class Employee  {

    private String name;
    private int salary;

    public Employee(String name, int salary) {
        this.name = name;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "name is: " + name + ", salary is: " + salary;
    }
    ...此处省略get/set
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这个时候我们显然无法修改Employee类了。但是还是需要对其排序,怎么办?

如果在jdk8之前,使用匿名内部类的方式:

    public static void test() {
        List<Employee> list = genList();
        Collections.sort(list, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return o1.getSalary() - o2.getSalary();
            }
        });
        System.out.println(list);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在jdk8之后,可以使用lambda表达式:

    public static void test() {
        List<Employee> list = genList();
        Comparator<Employee> comparator = (Employee e1, Employee e2) -> e1.getSalary() - e2.getSalary();
        list.sort(comparator);
        System.out.println(list);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

如果将此方法run起来,输出如下:

[name is: ccc, salary is: 80, name is: aaa, salary is: 100, name is: bbb, salary is: 150]
  • 1

同学们可能会注意到,Comparable接口中只有一个compareTo方法要实现,而Comparator有两个方法,但是我们只实现了一个方法,那么另外一个方法呢? 
其实很简单,因为另外一个方法是equals方法。所有的类都继承了Object类,而Object类中实现了equals方法,所以我们这里不实现equals方法也无所谓!

4.Comparator中的各种实现方式比较

Comparator中的compare方法实现方式还是比较多的。下面我们来一一说明。

4.1 传统的匿名内部类

JDK8之前,一般是采用匿名内部类的方式实现:

        Collections.sort(list, new Comparator<Employee>() {
            @Override
            public int compare(Employee o1, Employee o2) {
                return o1.getSalary() - o2.getSalary();
            }
        });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

4.2 lambda表达式

JDK8之后,可以使用lambda表达式:

list.sort((Employee e1, Employee e2) -> e1.getSalary() - e2.getSalary());
  • 1

4.3 精简版的lambda表达式

我们通过不指定类型定义来进一步简化表达式,因为编译器自己可以进行类型判断

list.sort((e1, e2) -> e1.getSalary() - e2.getSalary());
  • 1

4.4 使用Comparator.comparing的方式

我们使用上述lambda表达式的时候,IDE会提示我们:can be replaced with comparator.comparing Int

list.sort(Comparator.comparing(employee -> employee.getSalary()));
  • 1

4.5 使用静态方法的引用

java中的双冒号就是方法引用。::是JDK8里引入lambda后的一种用法,表示引用,比如静态方法的引用String::valueOf,比如构造器的引用,ArrayList::new。

list.sort(Comparator.comparing(Employee::getSalary));
  • 1

4.6 排序反转

很多时候,想对排序进行反转,或者说逆序:

list.sort(Comparator.comparing(Employee::getSalary).reversed());
  • 1

4.7 许多条件组合排序

        list.sort((e1, e2) -> {
            if(e1.getSalary() != e2.getSalary()) {
                return e1.getSalary() - e2.getSalary();
            } else {
                return e1.getName().compareTo(e2.getName());
            }
        });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4.8 从JDK 8开始,我们现在可以把多个Comparator链在一起(chain together)去建造更复杂的比较逻辑

list.sort(Comparator.comparing(Employee::getSalary).thenComparing(Employee::getName));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值