一. 問題背景
在瞭解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用來對map中的key排序。如果沒有實現comparator接口,TreeMap將使用默認實現的Comparable接口對key進行自然排序。
總結:這兩個接口都是用來對map中的key進行排序的
下面對Comparable和Comparator的知識點進行總結,此筆記僅供自己參考,如有錯誤請指正
爲了能正確的分析其知識點與區別,筆者儘量從源碼去分析。下面會涉及比較多的源碼(英文)
二. 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)來改變它的行爲。
參考博客:https://blog.csdn.net/mageshuai/article/details/3849143?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.nonecase