Comparable和Comparator的知識點以及兩者的區別

一. 問題背景

在瞭解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接口的對象能作爲SortedMapSortedSet的key(用這種key,SortedMapSortedSet就無需確定一個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也被用於數據結構中(比如SortedSetSortedMap、一些沒有實現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實現序列化也是非常好的,因爲它們經常被用於在數據結構(比如TreeSetTreeMap)排序方法中。爲了數據結構能成功地序列化,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的區別

  1. Comparator位於包java.util下,而Comparable位於包 java.lang下
  2. 使用Comparable接口實現排序,排序的規則(即重寫compareTo()方法)是定義在Bean類裏面
  3. 使用Comparator接口實現排序,排序的規則(即重寫compare()方法)是定義在外部(即不是Bean類裏面)
  4. 用 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章