文章目录
一. 问题背景
在了解TreeMap实现原理的过程中,了解到它必须实现Comparable或Comparator。为什么要实现这个接口,两者有什么区别?看到TreeMap源码有注释写着如下:
* Returns the comparator used to order the keys in this map, or
* {@code null} if this map uses the {@linkplain Comparable
* natural ordering} of its keys.
上面的注释说明了comparator用来对TreeMap中的key排序。如果没有实现comparator接口,TreeMap将使用key实现的Comparable接口对key进行自然排序。
总结:这两个接口都是用来对map中的key进行排序的
本文对Comparable和Comparator的知识点进行总结,此笔记仅供自己参考,如有错误请指正。为了能正确的分析其知识点与区别,笔者尽量从TreeMap源码去分析。下面会涉及比较多TreeMap的源码,英文也比较多。
二. Comparable
查看源码,如下:
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
由此得出 Comparable是在java.lang包下的。且只有一个方法compareTo(T o)
。
在
package java.lang;import java.util.*;
下面可以看到有一段implement notes
注释,我们从源码注释并结合他人的博客去学习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 <i>natural
* ordering</i>, and the class's <tt>compareTo</tt> method is referred to as
* its <i>natural comparison method</i>.<p>
大概意思:接口(指Comparable
接口)强制类(实现了Comparable
接口的类)中的对象 进行全排序。这个排序会被默认当作是类的自然排序(natural ordering
),并且类的compareTo()
方法被当作实现此排序的方法(排序的规则由此方法实现)。
Comparable的使用方法:
Collections.sort()
或Arrays.sort()
,如下:
Lists (and arrays) of objects that implement this interface can be sorted
* automatically by {@link Collections#sort(List) Collections.sort} (and
* {@link Arrays#sort(Object[]) Arrays.sort}). Objects that implement this
* interface can be used as keys in a {@linkplain SortedMap sorted map} or as
* elements in a {@linkplain SortedSet sorted set}, without the need to
* specify a {@linkplain Comparator comparator}.<p>
大概意思:由 实现了Comparable
接口的对象 组成的list或数组,可以被Collections.sort()
或Arrays.sort()
排序。现了Comparable
接口的对象能作为SortedMap
和SortedSet
的key(用这种key,SortedMap
和SortedSet
就无需确定一个Comparator
了)。
总结:从最后一句话可以得出Comparator
的优先级比Comparable
高。 即如果既有实现Comparable接口,又有实现Comparator接口,那么起到排序作用的是Comparator。
再继续看
public int compareTo(T o)
方法的注释。如下:
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.
大概意思:当前对象与确定的对象作比较,会返回一个负整数、0、正整数三者中的一个。并且负整数代表小于,0代表等于,正整数代表大于。
也就是说假如有
this.compareTo(o2)
,当:
返回值 | 代表的意思 |
---|---|
负整数 | this < o2 |
0 | this = 0 |
正整数 | this > o2 |
<p>In the foregoing description, the notation
* <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
* <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
* <tt>0</tt>, or <tt>1</tt> according to whether the value of
* <i>expression</i> is negative, zero or positive.
大概意思:根据上面的描述,方法的返回值:负整数,0,正整数,分别用-1,0,1代替。
@throws NullPointerException if the specified object is null
* @throws ClassCastException if the specified object's type prevents it
* from being compared to this object.
大概意思: 如果入参为null,抛出NullPointerException
; 如果入参的类型与当前对象的类型不一致,抛出ClassCastException
类型转换异常。
从上面得知Comparable是在java.lang包下的,联想到java的基础类(比如Integer)也是此包下,去查看有没有实现Comparable接口。如下:
public final class Integer extends Number implements Comparable<Integer> {
Integer实现了Comparable接口。因此我们可以看看其排序的规则默认是怎么样的?什么时候会返回-1、0、1?是默认升序还是降序?
找到
compareTo()
方法,如下:
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
点进去看
compare()
方法,如下:
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
可以看到x代表this.value,y是anotherInteger(即被比较的对象)。当前对象
<被比较对象
,返回-1;否则继续判断两者是否相等
,若相等返回0,否则返回1。
总结:也就是-1代表的实际意义是升序的,那么1就是降序的了。也可以理解成-1代表不进行排序,直接结束。1代表进行排序
2.1 例子
2.1.1 按User对象的age升序排序
首先User要实现
Comparable
接口,重写compareTo()
方法
User.java
package com.atguigu;
public class User implements Comparable<User>{
private String name;
private Integer age;
public User() {
}
public User(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(User o) {
return (this.age-o.age);
}
}
Main.java
@Test
public void test5() {
List<User> users = new ArrayList<>();
users.add(new User("tianqi", 7));
users.add(new User("zhangsan", 3));
users.add(new User("zhaoliu", 6));
users.add(new User("wuwang", 5));
System.out.println("排序前:" + users);
Collections.sort(users);
System.out.println("排序后" + users);
}
测试结果:
2.1.1 按User对象的age降序排序
只需将User中的
compareTo()
方法的排序规则修改即可,即修改return后的语句,用被比较对象.age-this.age
User.java
@Override
public int compareTo(User o) {
return (o.age-this.age);
}
测试结果:
总结:从实现Comparable进行排序的过程中,可以发现, 若使用Comparable进行排序,那么在定义类之处就必须考虑好排序的需求。当类定义好了,不能再随意修改,那么此时我要修改Comparable的排序规则是不推荐的。 比如Integer实现了Comparable接口,默认采用升序排序。我要改为降序,此时是显然不推荐去修改Comparable的排序规则(即jdk的源码)。那要怎么修改呢?使用Comparator接口
三. Comparator
package java.util;
Comparator
位于java.util下
源码的
implements notes
有这样一段,如下:
* A comparison function, which imposes a <i>total ordering</i> on some
* collection of objects. Comparators can be passed to a sort method (such
* as {@link Collections#sort(List,Comparator) Collections.sort} or {@link
* Arrays#sort(Object[],Comparator) Arrays.sort}) to allow precise control
* over the sort order. Comparators can also be used to control the order of
* certain data structures (such as {@link SortedSet sorted sets} or {@link
* SortedMap sorted maps}), or to provide an ordering for collections of
* objects that don't have a {@link Comparable natural ordering}.<p>
大概意思:该接口的功能是用于比较,它强制Objects集合进行全排序。通过Collections.sort(List,Comparator)
或Arrays.sort(Object[],Comparator)
方法,能进行精确的排序。 Comparators也被用于数据结构中(比如SortedSet
、SortedMap
、一些没有实现Comparable
接口的Objects集合)
Unlike {@code Comparable}, a comparator may optionally permit
* comparison of null arguments, while maintaining the requirements for
* an equivalence relation.
大概意思:与Comparable不同,comparator允许空参比较。
* Note: It is generally a good idea for comparators to also implement
* <tt>java.io.Serializable</tt>, as they may be used as ordering methods in
* serializable data structures (like {@link TreeSet}, {@link TreeMap}). In
* order for the data structure to serialize successfully, the comparator (if
* provided) must implement <tt>Serializable</tt>.<p>
大概意思:comparators实现序列化也是非常好的,因为它们经常被用于在数据结构(比如TreeSet
、TreeMap
)排序方法中。为了数据结构能成功地序列化,comparator必须实现Serializable
接口。
@FunctionalInterface
public interface Comparator<T> {
Comparator是一个函数式接口。可以使用lamda表达式。jdk1.8新增的内容
3.1 例子
3.1.1 对User对象的升序排序
@Test
public void test6() {
List<User> users = new ArrayList<>();
users.add(new User("tianqi", 7));
users.add(new User("zhangsan", 3));
users.add(new User("zhaoliu", 6));
users.add(new User("wuwang", 5));
System.out.println("排序前" + users);
Collections.sort(users, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return (o1.getAge()-o2.getAge());
// return (o2.getAge()-o1.getAge());
}
});
System.out.println("排序后" + users);
}
测试结果:.
3.1.2 对User对象的降序排序
只需修改return后的语句即可,如下:
@Override
public int compare(User o1, User o2) {
// return (o1.getAge()-o2.getAge());
return (o2.getAge()-o1.getAge());
}
3.1.3 对User对象的年龄为null排序
与Comparable不同,Comparator允许空参排序。需求:对User对象的age进行降序排序,age为null的排在最后。
@Test
public void test6() {
List<User> users = new ArrayList<>();
users.add(new User("tianqi", null));
users.add(new User("zhangsan", 3));
users.add(new User("zhaoliu", null));
users.add(new User("wuwang", 5));
System.out.println("排序前" + users);
Collections.sort(users, new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
if(o1.getAge() != null && o2.getAge() != null) {
return (o2.getAge()-o1.getAge());
}else {
return o1.getAge() == null ? 1 : -1;
}
}
});
System.out.println("排序后" + users);
}
测试结果:
需求:对User对象的age进行降序排序,age为null的排在最前。
只需将compare()中的第二个return改为
return o1.getAge() == null ? -1 : 1;
,如下:
public int compare(User o1, User o2) {
if(o1.getAge() != null && o2.getAge() != null) {
return (o2.getAge()-o1.getAge());
}else {
return o1.getAge() == null ? -1 : 1;
}
}
测试结果:
总结:使用Comparator接口排序,需要在集合外定义Comparator接口的方法
四. Comparable和Comparator的区别
- Comparator位于包java.util下,而Comparable位于包 java.lang下
- 使用Comparable接口实现排序,排序的规则(即重写
compareTo()
方法)是定义在Bean类里面 - 使用Comparator接口实现排序,排序的规则(即重写
compare()
方法)是定义在外部(即不是Bean类里面) - 用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。