以前一直对Comparable与Comparator的区别比较模糊,今天抽空好好的了解了下,发现收获蛮大的,所以来跟大家分享下,如果有不对的地方还请指正。
先来看看这2个接口在jdk API种的解释:
Comparable:此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
实现此接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
Comparator:强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。从这句话可以看出我们之所以可以对数字或者字母进行排序而不需制定比较器,是因为在JDK中他们已经实现了Comparable接口,所以通过Collections.sort和 Arrays.sort方法可以给他们按自然顺序排序,当我们要对一个自定义对象进行排序的时候,也可以实现Comparable接口,实现它的compareTo方法。
1 @Override
2 public int compareTo(User user) {
3 if(this == null || user == null || this.getAge() > user.getAge())
4 return 1;
5 if(this.getAge() < user.getAge())
6 return -1;
7 return 0;
8 }
如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数,可以通过返回的值来控制对象在集合的位置,可以按年龄的小到大排序,也可以按年龄的大到小排序。
如果要用Comparator接口来实现对象在集合中的排序,直接在对象中实现Comparator接口是不起作用的(针对TreeSet,下文中一样,其它的集合类还没发现可以在对象中实现Comparator来进行排序),需要另外新建一个类实现Comparator接口,然后通过集合的构造函数传到集合中,才会起作用,直接看TreeSet的源代码吧。
1 int cmp;
2 Entry<K,V> parent;
3 // split comparator and comparable paths
4 Comparator<? super K> cpr = comparator;
5 if (cpr != null) {
6 do {
7 parent = t;
8 cmp = cpr.compare(key, t.key);
9 if (cmp < 0)
10 t = t.left;
11 else if (cmp > 0)
12 t = t.right;
13 else
14 return t.setValue(value);
15 } while (t != null);
16 }
17 else {
18 if (key == null)
19 throw new NullPointerException();
20 Comparable<? super K> k = (Comparable<? super K>) key;
21 do {
22 parent = t;
23 cmp = k.compareTo(t.key);
24 if (cmp < 0)
25 t = t.left;
26 else if (cmp > 0)
27 t = t.right;
28 else
29 return t.setValue(value);
30 } while (t != null);
31 }
32 Entry<K,V> e = new Entry<K,V>(key, value, parent);
33 if (cmp < 0)
34 parent.left = e;
35 else
36 parent.right = e;
37 fixAfterInsertion(e);
38 size++;
39 modCount++;
40 return null;
comparator是在构造TreeSet对象的时候传进去的,所以如果comparator为null,它就会按照Comparable排序,如果对象没有实现Comparable接口,就会抛出异常,所以到现在为止,我还没发现直接在对象中实现comparator接口可以排序的。看完这段代码我们还可以发现如果对象没有实现Comparable接口或者没有通过构造一个以comparator为排序的集合,是不能在TreeSet或TreeMap中使用的(实际TreeSet就是通过TreeMap实现的,不了解的可以看TreeSet的源代码),最多只能添加一个对象root。
1 Entry<K,V> t = root;
2 if (t == null) {
3 // TBD:
4 // 5045147: (coll) Adding null to an empty TreeSet should
5 // throw NullPointerException
6 //
7 // compare(key, key); // type check
8 root = new Entry<K,V>(key, value, null);
9 size = 1;
10 modCount++;
11 return null;
12 }
comparator的用处在于可以通过不同的comparator实现类,在不通的条件下对在集合中的对象按不同的顺序进行排序,这样充分利用了java的多态,也让我们在编程的过程中能很好的解耦。
利用Arrays.sort(T[] a, Comparator<? super T> c)可以对数组进行排序,利用Collections.sort(List<T> list, Comparator<? super T> c) 可以对实现了list接口的集合进行排序,如果需要对Set进行排序,可以先把Set转换成ArrayList,然后再进行排序。
1 Set<User> users = new TreeSet<User>(new UserComparator());
2 User user = new User();
3 user.setAge(24);
4 user.setName("wenbo");
5 users.add(user);
6 User user1 = new User();
7 user1.setAge(23);
8 user1.setName("wenbo1");
9 users.add(user1);
10 List<User> useList = new ArrayList<User>(users);
11 Collections.sort(useList,new UserMaxComparator());
通过上面的分析,不知道大家有没有理解这2个接口,我认为如果需要对一个对象进行排序,建议使用
comparator接口,因为它更灵活,或许效率更高。