本文用来简单记录JavaComparatorComparable接口特点与使用。

首先,分别查看官方对这两个接口的描述定义:

一、Comparator接口

This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.

A comparison function, which imposes a total ordering on some collection of objects.

可以看出官方对该接口的描述是,它是一种比较函数,用于对一些集合中的对象进行总的排序。即:对集合中的元素进行排序。通过其源码可以看出,除了compare(T o1,T o2)方法,其他方法都给了默认实现。

1
int compare(T o1, T o2);

所以要想使用该接口,就得实现该接口的此方法。那么,该接口的作用是什么呢?

实现接口Comparator<T>类型的任何类都必须要有一个compare的方法,该方法有两个泛型类型(AnyType)的参数 并返回一个int型的量,遵守和compareTo(Comparable接口中需要实现的方法)相同的约定。

简而言之,是对集合中的元素,进行比较。该接口用来返回int类型的结果。当返回是一个正数时,表明o1在集合中的排列在o2之前(注意:这里只是说o1排列在o2之前,并没有说o1大于/小于o2,因此这里的比较,只是用来确定元素在集合中的排列顺序,说大小的比较其实并不太准确);当返回是一个负数时,表明o2在集合中排列在o1之前;如果返回0,那么o1o2的排列先后顺序没有关系,简单理解成两个对象”相等”。

我们以最简单的例子,来对上面的说法进行实践:

Java中的java.util.Arrays中的sort方法如果对int类型的数组进行排序,默认是升序排序,这里我们自定义一个Comparator接口来实现降序排序。

  1. 自定义Comparator接口
1
2
3
4
5
6
7
8
9
10
11
12
public class MyComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
if (o1 - o2 > 0) { // 该条件表明o1大于o2,o1排列在o2之后
return -1;
} else if (o1 - o2 < 0) { // 该条件表明o1小于o2,o1排列在o2之前
return 1;
} else { // 顺序无关
return 0;
}
}
}
  1. 使用自定的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyComparatorTest {

public static void main(String[] args) {
Random random = new Random();
Integer[] res = new Integer[20];

for (int i = 0; i < 20; ++i) {
res[i] = random.nextInt(50) + 50; // 随机生成 50 到 100 之间的随机整数
}
Arrays.sort(res, new MyComparator());
System.out.println(Arrays.toString(res));
}
}

=====结果=====
[96, 87, 87, 85, 84, 80, 80, 78, 78, 74, 68, 65, 65, 65, 64, 63, 53, 53, 53, 51]

所以,Comparator用于指定集合中对象的排列方式,并且我们在使用Comparator接口时,是专门定义类(工具类)去实现该接口,使得该工具类与被比较对象的类进行了分离。如果对被需要比较的对象修改了比较策略,那么只需要修改我们的工具类即可,因此大大降低了对象之间的耦合性(与Comparable接口最大的不同之处),更具有灵活性。

二、Comparable接口

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.

从上面的实现中看出,对实现该接口的对象进行一个”总”的排序。即:如果想要使用该接口的排列策略,让需要排列的对象的类实现该接口。

其源码:

1
2
3
4
5
6
/**
* Compares this object with the specified object for order. Returns a
* negative integer, zero, or a positive integer as this object is less
* than, equal to, or greater than the specified object.
**/
public int compareTo(T o);

使用方式同Comparator接口,只是这里是实现compareTo接口。

这里我们自定义一个People类:

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
37
public class People  implements Comparable<Object>{

private int age;
private String name;
private double salary;

public People() {
}

public People(int age, String name, double salary) {
this.age = age;
this.name = name;
this.salary = salary;
}

/**
getter / setter / toString 方法的省略
**/

@Override
public int compareTo(Object o) {
People tempPeople = (People) o;
if (this.age > tempPeople.getAge()) {
return 1;
} else if (this.age < tempPeople.getAge()) {
return -1;
}

if (this.salary > tempPeople.getSalary()) {
return 1;
} else if (this.salary < tempPeople.getSalary()) {
return -1;
}

return 0;
}
}

我们看出我们自定义的People类实现了Comparable接口,实现的比较规则是,先按照年龄的大小进行排序,如果年龄大小相同,按照工资的大小进行排序,否则最终返回0.(代表此时两个对象”相等”)。

接下来来对该类进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void test() {

People p1 = new People(24, "Lisa", 4000d);
People p2 = new People(24, "Lisa", 5000d);
People p3 = new People(25, "Lisa", 5000d);

People[] people = {p1, p2, p3};
Arrays.sort(people);
System.out.println(Arrays.toString(people));

}
====结果====
[People{age=24, name='Lisa', salary=4000.0}, People{age=24, name='Lisa', salary=5000.0}, People{age=25, name='Lisa', salary=5000.0}]

三、总结

Comparable接口和Comparator接口区别

  1. Comparator位于包Java.util下,而Comparable位于包Java.lang包下。
  2. Comparable接口将比较代码嵌入需要进行比较的自身代码中,而Comparator接口在一个独立的类中实现比较。
  3. Comparable接口需要实现重写comparaTo方法,同时Comparable是排序接口,若一个类实现了Comparable接口,该类支持排序,相当于内部比较器,Comparator相当于外部比较器。Comparable接口强制进行自然排序,Comparator不强制,可以指定排序。
  4. Comparator更适用于Java提供的类使用;Comparable适用我们自定义的类使用(嵌入在自定义的类中)

Comment