Java基礎系列-Comparable和Comparator


原創(chuàng)文章,轉(zhuǎn)載請標注出處:《Java基礎系列-Comparable和Comparator》


一、概述

????????Java中的排序是由Comparable和Comparator這兩個接口來提供的。

????????Comparable表示可被排序的完丽,實現(xiàn)該接口的類的對象自動擁有排序功能。

????????Comparator則表示一個比較器,實現(xiàn)了該接口的的類的對象是一個針對目標類的對象定義的比較器丛楚,一般情況,這個比較器將作為一個參數(shù)進行傳遞憔辫。

二趣些、Comparable

????????Comparable的中文意思就是可被排序的,代表本身支持排序功能贰您。只要我們的類實現(xiàn)了這個接口坏平,那么這個類的對象就會自動擁有了可被排序的能力。而且這個排序被稱為類的自然順序锦亦。這個類的對象的列表可以被Collections.sort和Arrays.sort來執(zhí)行排序舶替。同時這個類的實例具備作為sorted map的key和sorted set的元素的資格。

????????假如a和b都是實現(xiàn)了Comparable接口的類C的實例杠园,那么只有當a.compareTo(b)的結果與a.equals(b)的結果一致時顾瞪,才稱類C的自然順序與equals一致。強烈建議將類的自然順序和equals的結果保持一致,因為如果不一致的話陈醒,由該類對象為鍵的sorted map和由該類對象為元素的sorted set的行為將會變得很怪異惕橙。

????????例如對于一個實現(xiàn)了Comparable接口的元素的有序集合sorted set而言,如果a.equals(b)結果為false钉跷,并且a.compareTo(b)==0弥鹦,則第二個元素的添加操作將會失敗,因為在sorted set看來爷辙,二者在排序上是一致的彬坏,它不報保存重復的元素。

????????事實上膝晾,Java中的類基本都是自然順序與equals一致的栓始,除了BigDecimal,因為BigDecimal中的自然順序和值相同但精度不同的元素(例如4和4.00)的equals均一致玷犹。

源碼解析

public interface Comparable<T> {
    public int compareTo(T o);
}

????????從源碼中可以看到混滔,該接口只有一個抽象方法compareTo,這個方法主要就是為了定義我們的類所要排序的方式歹颓。compareTo方法用于比較當前元素a與指定元素b坯屿,結果為int值,如果a > b巍扛,int>0领跛;如果a=b,int=0撤奸;如果a<b吠昭,int<0。

三胧瓜、Comparator

????????Comparator中文譯為比較器矢棚,它可以作為一個參數(shù)傳遞到Collections.sort和Arrays.sort方法來指定某個類對象的排序方式。同時它也能為sorted set和sorted map指定排序方式府喳。

????????同Comparable類似蒲肋,指定比較器的時候一般也要保證比較的結果與equals結果一致,不一致的話钝满,對應的sorted set和sorted map的行為同樣會變得怪異兜粘。

????????推薦實現(xiàn)的比較器類同時實現(xiàn)java.io.Serializable接口,以擁有序列化能力弯蚜,因為它可能會被用作序列化的數(shù)據(jù)結構(TreeSet孔轴、TreeMap)的排序方法。

源碼解析

@FunctionalInterface
public interface Comparator<T> {
    // 唯一的抽象方法碎捺,用于定義比較方式(即排序方式)
    // o1>o2路鹰,返回1贷洲;o1=o2,返回0晋柱;o1<o2恩脂,返回-1
    int compare(T o1, T o2);
    boolean equals(Object obj);
    // 1.8新增的默認方法:用于反序排列
    default Comparator<T> reversed() {
        return Collections.reverseOrder(this);
    }
    // 1.8新增的默認方法:用于構建一個次級比較器,當前比較器比較結果為0趣斤,則使用次級比較器比較
    default Comparator<T> thenComparing(Comparator<? super T> other) {
        Objects.requireNonNull(other);
        return (Comparator<T> & Serializable) (c1, c2) -> {
            int res = compare(c1, c2);
            return (res != 0) ? res : other.compare(c1, c2);
        };
    }
    // 1.8新增默認方法:指定次級比較器的
    // keyExtractor表示鍵提取器,定義提取方式
    // keyComparator表示鍵比較器黎休,定義比較方式
    default <U> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        return thenComparing(comparing(keyExtractor, keyComparator));
    }
    // 1.8新增默認方法:用于執(zhí)行鍵的比較浓领,采用的是由鍵對象內(nèi)置的比較方式
    default <U extends Comparable<? super U>> Comparator<T> thenComparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        return thenComparing(comparing(keyExtractor));
    }
    // 1.8新增默認方法:用于比較執(zhí)行int類型的鍵的比較
    default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
        return thenComparing(comparingInt(keyExtractor));
    }
    // 1.8新增默認方法:用于比較執(zhí)行l(wèi)ong類型的鍵的比較
    default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) {
        return thenComparing(comparingLong(keyExtractor));
    }
    // 1.8新增默認方法:用于比較執(zhí)行double類型的鍵的比較
    default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) {
        return thenComparing(comparingDouble(keyExtractor));
    }
    // 1.8新增靜態(tài)方法:用于得到一個相反的排序的比較器,這里針對的是內(nèi)置的排序方式(即繼承Comparable)
    public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
        return Collections.reverseOrder();
    }
    // 1.8新增靜態(tài)方法:用于得到一個實現(xiàn)了Comparable接口的類的比較方式的比較器
    // 簡言之就是將Comparable定義的比較方式使用Comparator實現(xiàn)
    @SuppressWarnings("unchecked")
    public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
        return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
    }
    // 1.8新增靜態(tài)方法:得到一個null親和的比較器势腮,null小于非null联贩,兩個null相等,如果全不是null,
    // 則使用指定的比較器比較捎拯,若未指定比較器泪幌,則非null全部相等返回0
    public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(true, comparator);
    }
    // 1.8新增靜態(tài)方法:得到一個null親和的比較器,null大于非null署照,兩個null相等祸泪,如果全不是null,
    // 則使用指定的比較器比較,若未指定比較器建芙,則非null全部相等返回0
    public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
        return new Comparators.NullComparator<>(false, comparator);
    }
    // 1.8新增靜態(tài)方法:使用指定的鍵比較器用于執(zhí)行鍵的比較
    public static <T, U> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor,
            Comparator<? super U> keyComparator)
    {
        Objects.requireNonNull(keyExtractor);
        Objects.requireNonNull(keyComparator);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
                                              keyExtractor.apply(c2));
    }
    // 1.8新增靜態(tài)方法:執(zhí)行鍵比較没隘,采用內(nèi)置比較方式,key的類必須實現(xiàn)Comparable
    public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
    }
    // 1.8新增靜態(tài)方法:用于int類型鍵的比較
    public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
    }
    // 1.8新增靜態(tài)方法:用于long類型鍵的比較
    public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
    }
    // 1.8新增靜態(tài)方法:用于double類型鍵的比較
    public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
    }
}

????????老版本的Comparator中只要兩個方法禁荸,就是前兩個方法右蒲,后面的所有默認方法均為1.8新增的方法,采用的是1.8新增的功能:接口可添加默認方法赶熟。即便擁有如此多方法瑰妄,該接口還是函數(shù)式接口,compare用于定義比較方式映砖。

四间坐、二者比較

????????Comparable可以看做是內(nèi)部比較器,Comparator可以看做是外部比較器啊央。

????????一個類眶诈,可以通過實現(xiàn)Comparable接口來自帶有序性,也可以通過額外指定Comparator來附加有序性瓜饥。

????????二者的作用其實是一致的逝撬,所以不要混用。

????????我們看個例子吧:

????????首先定義個模型:User

public class User implements Serializable, Comparable<User> {
    private static final long serialVersionUID = 1L;
    private int age;
    private String name;
    public User (){}
    public User (int age, String name){
        this.age = age;
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public int compareTo(User o) {
        return this.age - o.age;
    }
    @Override
    public String toString() {
        return "[user={age=" + age + ",name=" + name + "}]";
    }
}

????????在定義一個Comparator實現(xiàn)類MyComparator

public class MyComparator implements Comparator<User> {
    @Override
    public int compare(User o1, User o2) {
        return o1.getName().charAt(0)-o2.getName().charAt(0);
    }
}

????????最后是測試類:Main

public class Main {
    public static void main(String[] args) {
        User u1 = new User(12, "xiaohua");
        User u2 = new User(10, "abc");
        User u3 = new User(15,"ccc");
        User[] users = {u1,u2,u3};
        System.out.print("數(shù)組排序前:");
        printArray(users);
        System.out.println();
        Arrays.sort(users);
        System.out.print("數(shù)組排序1后:");
        printArray(users);
        System.out.println();
        Arrays.sort(users, new MyComparator());
        System.out.print("數(shù)組排序2后:");
        printArray(users);
        System.out.println();
        Arrays.sort(users, Comparator.reverseOrder());// 針對內(nèi)置的排序進行倒置
        System.out.print("數(shù)組排序3后:");
        printArray(users);
    }
    public static void printArray (User[] users) {
        for (User user:users) {
            System.out.print(user.toString());
        }
    }
}

????????運行結果為:

數(shù)組排序前:[user={age=12,name=xiaohua}][user={age=10,name=abc}][user={age=15,name=ccc}]
數(shù)組排序1后:[user={age=10,name=abc}][user={age=12,name=xiaohua}][user={age=15,name=ccc}]
數(shù)組排序2后:[user={age=10,name=abc}][user={age=15,name=ccc}][user={age=12,name=xiaohua}]
數(shù)組排序3后:[user={age=15,name=ccc}][user={age=12,name=xiaohua}][user={age=10,name=abc}]

????????通過上面的例子我們有一個結論乓土,那就是兩種方式定義排序的優(yōu)先級宪潮,明顯Comparator比較器要優(yōu)先于內(nèi)部排序Comparable溯警。

五、總結

  • Comparable為可排序的狡相,實現(xiàn)該接口的類的對象自動擁有可排序功能梯轻。
  • Comparator為比較器,實現(xiàn)該接口可以定義一個針對某個類的排序方式尽棕。
  • Comparator與Comparable同時存在的情況下喳挑,前者優(yōu)先級高。

參考:

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末滔悉,一起剝皮案震驚了整個濱河市伊诵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌回官,老刑警劉巖曹宴,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異歉提,居然都是意外死亡笛坦,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門苔巨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來版扩,“玉大人,你說我怎么就攤上這事恋拷∽世鳎” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵蔬顾,是天一觀的道長宴偿。 經(jīng)常有香客問我,道長诀豁,這世上最難降的妖魔是什么窄刘? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮舷胜,結果婚禮上娩践,老公的妹妹穿的比我還像新娘。我一直安慰自己烹骨,他們只是感情好翻伺,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著沮焕,像睡著了一般吨岭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上峦树,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天辣辫,我揣著相機與錄音旦事,去河邊找鬼。 笑死急灭,一個胖子當著我的面吹牛姐浮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播葬馋,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼扒怖,長吁一口氣:“原來是場噩夢啊……” “哼垢啼!你這毒婦竟也來了檀夹?” 一聲冷哼從身側響起悴品,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎掠廓,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體甩恼,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡蟀瞧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了条摸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悦污。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖钉蒲,靈堂內(nèi)的尸體忽然破棺而出切端,到底是詐尸還是另有隱情,我是刑警寧澤顷啼,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布踏枣,位于F島的核電站,受9級特大地震影響钙蒙,放射性物質(zhì)發(fā)生泄漏茵瀑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一躬厌、第九天 我趴在偏房一處隱蔽的房頂上張望马昨。 院中可真熱鬧,春花似錦扛施、人聲如沸鸿捧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽匙奴。三九已至,卻和暖如春昌阿,著一層夾襖步出監(jiān)牢的瞬間饥脑,已是汗流浹背恳邀。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留灶轰,地道東北人谣沸。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像笋颤,于是被迫代替她去往敵國和親乳附。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容