淺析Collections.sort

問(wèn)題引入

??在之前的一次Java上機(jī)實(shí)習(xí)中,老師布置了一道很簡(jiǎn)單的題:

從控制臺(tái)輸入10個(gè)整數(shù),對(duì)它們進(jìn)行升序排序并輸出膊毁。

??考慮到只有10個(gè)數(shù),需要比較的次數(shù)不是很多基跑,所以當(dāng)時(shí)我自己寫(xiě)了一段冒泡排序的算法然后就上交作業(yè)婚温。交作業(yè)之后我突然想到一個(gè)問(wèn)題:JDK是否有類(lèi)似于STL中的std::sort方法,能實(shí)現(xiàn)基本的排序而無(wú)需用戶(hù)自己實(shí)現(xiàn)媳否?

Collections.sort簡(jiǎn)介

??第9版《Java核心技術(shù)卷Ⅰ》的第607頁(yè)介紹了一個(gè)方法:

Collections類(lèi)中的sort方法可以對(duì)實(shí)現(xiàn)了List接口的集合進(jìn)行排序栅螟。這個(gè)方法假定列表元素實(shí)現(xiàn)了Comparable接口荆秦。

??查看Java官方文檔可知,sort方法有兩種重載形式力图。第一種重載是:

static <T extends Comparable<? super T>> void sort(List<T> list)

??根據(jù)官方文檔的描述步绸,這個(gè)方法將列表元素進(jìn)行升序排序,但是列表要滿(mǎn)足以下條件:
??1.列表元素實(shí)現(xiàn)了Comparable接口吃媒,且任意兩個(gè)列表元素都是可比的瓤介。
??2.列表必須支持set方法。
??如果要通過(guò)這個(gè)方法來(lái)完成我上面提到的作業(yè)赘那,那顯然是行得通的:整數(shù)可以進(jìn)行相互比較刑桑,第一個(gè)條件滿(mǎn)足;把10個(gè)整數(shù)放入一個(gè)ArrayList或LinkedList中募舟,這兩個(gè)列表都支持set方法祠斧,第二個(gè)條件滿(mǎn)足。實(shí)現(xiàn)代碼如下:

import java.util.*;
public class Sort {

    public static void main(String[] args) {
        
        Scanner scan = new Scanner(System.in);
        List<Integer> list = new ArrayList<>();          
        
        //用戶(hù)輸入10個(gè)整數(shù)
        System.out.println("請(qǐng)輸入10個(gè)整數(shù):");
        for(int i = 0; i < 10; i++)                      
        {
            list.add(scan.nextInt());
        }
        scan.close();
        
        //排序
        Collections.sort(list);
        
        //輸出排序結(jié)果
       System.out.println(list);
    }

}

??程序運(yùn)行結(jié)果如下:

用sort進(jìn)行升序排序

??用sort方法可以很方便地實(shí)現(xiàn)升序排序拱礁,但能否進(jìn)行降序排序琢锋?答案是肯定的,《Java核心技術(shù)卷Ⅰ》中介紹了一種用sort進(jìn)行降序排序的簡(jiǎn)潔方法呢灶。這種方法需要用到sort方法的第二種重載形式:

public static <T> void sort(List<T> list,Comparator<? super T> c)

??如果想采用其他方式進(jìn)行排序吴超,那么可將一個(gè)Comparator對(duì)象作為sort方法的第二個(gè)參數(shù)。當(dāng)要進(jìn)行逆序排序時(shí)填抬,最簡(jiǎn)便的方法是將Collections.reverseOrder()作為第二個(gè)參數(shù)烛芬。

import java.util.*;
public class Sort {

    public static void main(String[] args) {
        
        Scanner scan = new Scanner(System.in);
        List<Integer> list = new ArrayList<>();          
        
        //用戶(hù)輸入10個(gè)整數(shù)
        System.out.println("請(qǐng)輸入10個(gè)整數(shù):");
        for(int i = 0; i < 10; i++)                      
        {
            list.add(scan.nextInt());
        }
        scan.close();
        
        //逆序排序
        Collections.sort(list,Collections.reverseOrder());
        
        //輸出排序結(jié)果
        System.out.println(list);
    }

}

??程序運(yùn)行結(jié)果如下:


用sort進(jìn)行降序排序

更進(jìn)一步:排序?qū)ο蟛皇腔緮?shù)據(jù)類(lèi)型

??在上面的例子中,列表中的元素是整數(shù)飒责,所以排序邏輯是很顯然的:如果是升序排序赘娄,那就小數(shù)在前,大數(shù)在后宏蛉。但是遣臼,如果排序的對(duì)象并非屬于整數(shù)、浮點(diǎn)數(shù)之類(lèi)的基本數(shù)據(jù)類(lèi)型拾并,那么這些對(duì)象之間的“大小”關(guān)系該如何定義揍堰?假設(shè)有這樣一道題:

定義一個(gè)點(diǎn)類(lèi),其中有整型屬性x和y嗅义,代表其坐標(biāo)屏歹;除了這兩個(gè)屬性以外沒(méi)有其他屬性。隨機(jī)產(chǎn)生10個(gè)點(diǎn)之碗,并按照這些點(diǎn)與原點(diǎn)(0,0)之間的距離大小對(duì)點(diǎn)進(jìn)行降序排序蝙眶。

??如果仍想通過(guò)sort方法進(jìn)行排序的話(huà),首先點(diǎn)類(lèi)就必須滿(mǎn)足上面曾經(jīng)提過(guò)的約束條件:點(diǎn)對(duì)象是可比的褪那,因此點(diǎn)類(lèi)必須實(shí)現(xiàn)Comparable接口幽纷。查看官方文檔可知式塌,Comparable接口中只有一個(gè)方法:

int compareTo(T o)

??調(diào)用這個(gè)方法的對(duì)象將會(huì)與參數(shù)o進(jìn)行比較,小于o友浸、等于o和大于o分別對(duì)應(yīng)的返回值為負(fù)數(shù)峰尝、0和正數(shù)。對(duì)象之間相對(duì)大小的判斷方法是自定義的收恢,在這個(gè)問(wèn)題中武学,就是通過(guò)比較各點(diǎn)與原點(diǎn)之間的距離來(lái)判斷大小,所以點(diǎn)類(lèi)的實(shí)現(xiàn)如下:

class Point implements Comparable<Point>{
    
    private int x;
    private int y;
    
    public Point(int x,int y)
    {
        this.x = x;
        this.y = y;
    }
    
    @Override
    //如果該點(diǎn)到原點(diǎn)的距離大于o點(diǎn)到原點(diǎn)的距離派诬,則該點(diǎn)大于o點(diǎn)
    public int compareTo(Point o) {

        int distance1 = (this.x) * (this.x) + (this.y) * (this.y);
        int distance2 = (o.x) * (o.x) + (o.y) * (o.y);
        
        return (distance1 > distance2) ? 1 : ((distance1 == distance2) ? 0 : -1); 
    }
    
    @Override
    public String toString() {
        return "(" + x + ","+  y + ")";
    }
}

??因?yàn)橐M(jìn)行降序排序劳淆,所以可以通過(guò)將Collections.reverseOrder()作為sort方法的第二個(gè)參數(shù)來(lái)實(shí)現(xiàn):

public class SortDemo {

    private static List<Point> list = new ArrayList<>();
    
    public static void main(String[] args) {
        
        //隨機(jī)生成10個(gè)點(diǎn)
        for(int i = 0; i < 10; i++)
        {
            //點(diǎn)的坐標(biāo)取值在[1,20]之間
            int x = (int)(Math.random() * 20) + 1;
            int y = (int)(Math.random() * 20) + 1;
            
            list.add(new Point(x,y));
        }
        System.out.print("排序前:");
        System.out.println(list);
        
        //降序排序
        Collections.sort(list,Collections.reverseOrder());
        
        System.out.print("排序后:");
        System.out.println(list);
    }

}

??程序結(jié)果如下:


對(duì)點(diǎn)進(jìn)行降序排序

??現(xiàn)在對(duì)sort方法進(jìn)行小結(jié):

??實(shí)現(xiàn)了Comparable接口的類(lèi)都可以用sort方法進(jìn)行排序,默認(rèn)的排序方法是升序默赂;如果想進(jìn)行降序排序,只需把Collections.reverseOrder作為第二個(gè)參數(shù)傳給sort方法括勺。

了解Comparator接口

??上面反復(fù)提到的Collections.reverseOrder方法返回的是一個(gè)Comparator對(duì)象缆八。其實(shí)Comparator接口并不陌生,常用的equals方法就來(lái)自這個(gè)接口疾捍。Comparator接口用來(lái)定義兩個(gè)對(duì)象之間的比較方法奈辰,它有一個(gè)叫做compare的方法,函數(shù)簽名如下:

int compare(T o1,T o2)

?? o1 > o2乱豆,返回正數(shù)奖恰;o1 = o2,返回0宛裕;o1 < o2瑟啃,返回負(fù)數(shù)。
??從前面的例子可以看出揩尸,可以使用Comparator對(duì)象來(lái)控制sort的排序方法蛹屿,這是如何實(shí)現(xiàn)的?查看sort方法的相關(guān)源碼岩榆,我發(fā)現(xiàn)其中有這樣一段代碼:

sort方法的相關(guān)源碼

??注意看圖中用紅線(xiàn)框起來(lái)的部分错负。經(jīng)分析可知,這段代碼實(shí)現(xiàn)了這樣的邏輯:如果compare的返回值為正數(shù)勇边,就交換進(jìn)行比較的兩個(gè)元素的位置犹撒。于是可以得出這樣一個(gè)結(jié)論,如果讓 x > y 時(shí)compare(x,y)返回正數(shù)粒褒,那么交換 x 和 y 的位置后大的元素在后识颊,這就實(shí)現(xiàn)了升序排序;反之怀浆,如果讓 x < y 時(shí)compare(x,y)返回正數(shù)谊囚,那么交換位置后小的元素在后怕享,這就實(shí)現(xiàn)了降序排序。這就是Comparator對(duì)象控制排序方式的原理镰踏。

Comparator對(duì)象控制排序方式的原理

??通過(guò)Comparator對(duì)象來(lái)實(shí)現(xiàn)點(diǎn)對(duì)象的降序排序函筋,一種可行的實(shí)現(xiàn)方式如下:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

//點(diǎn)類(lèi)
class Point {
    
    private int x;
    private int y;
    
    public Point(int x,int y)
    {
        this.x = x;
        this.y = y;
    }
    
    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
    
    @Override
    public String toString() {
        return "(" + x + ","+  y + ")";
    }
}

public class SortDemo {

    private static List<Point> list = new ArrayList<>();
    
    public static void main(String[] args) {
        
        //隨機(jī)生成10個(gè)點(diǎn)
        for(int i = 0; i < 10; i++)
        {
            //點(diǎn)的坐標(biāo)取值在[1,20]之間
            int x = (int)(Math.random() * 20) + 1;
            int y = (int)(Math.random() * 20) + 1;
            
            list.add(new Point(x,y));
        }
        System.out.print("排序前:");
        System.out.println(list);
        
        //降序排序
        Collections.sort(list,new Comparator<Point>() {

            @Override
            //當(dāng) o1 < o2 時(shí)返回正數(shù)
            public int compare(Point o1, Point o2) {
                int distance1 = (o1.getX()) * (o1.getX()) + (o1.getY()) * (o1.getY());
                int distance2 = (o2.getX()) * (o2.getX()) + (o2.getY()) * (o2.getY());
                
                return (distance1 < distance2) ? 1 : ((distance1 == distance2) ? 0 : -1); 
            }
            
        });
        
        System.out.print("排序后:");
        System.out.println(list);
    }

}

??程序運(yùn)行結(jié)果如下:

通過(guò)實(shí)現(xiàn)Comparator接口來(lái)進(jìn)行降序排序

??注意,在上面的程序中Point類(lèi)并沒(méi)有實(shí)現(xiàn)Comparable接口奠伪,這是因?yàn)橐呀?jīng)通過(guò)實(shí)現(xiàn)Comparator接口來(lái)定義Point對(duì)象的比較方法跌帐,所以也就無(wú)需實(shí)現(xiàn)Comparable接口。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末绊率,一起剝皮案震驚了整個(gè)濱河市谨敛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌滤否,老刑警劉巖脸狸,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異藐俺,居然都是意外死亡炊甲,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)欲芹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)卿啡,“玉大人,你說(shuō)我怎么就攤上這事菱父【蹦龋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵浙宜,是天一觀(guān)的道長(zhǎng)官辽。 經(jīng)常有香客問(wèn)我,道長(zhǎng)梆奈,這世上最難降的妖魔是什么野崇? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮亩钟,結(jié)果婚禮上乓梨,老公的妹妹穿的比我還像新娘。我一直安慰自己清酥,他們只是感情好扶镀,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著焰轻,像睡著了一般臭觉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,760評(píng)論 1 289
  • 那天蝠筑,我揣著相機(jī)與錄音狞膘,去河邊找鬼。 笑死什乙,一個(gè)胖子當(dāng)著我的面吹牛挽封,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播臣镣,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼辅愿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了忆某?” 一聲冷哼從身側(cè)響起点待,我...
    開(kāi)封第一講書(shū)人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弃舒,沒(méi)想到半個(gè)月后癞埠,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡聋呢,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年燕差,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片坝冕。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瓦呼,靈堂內(nèi)的尸體忽然破棺而出喂窟,到底是詐尸還是另有隱情,我是刑警寧澤央串,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布磨澡,位于F島的核電站,受9級(jí)特大地震影響质和,放射性物質(zhì)發(fā)生泄漏稳摄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一饲宿、第九天 我趴在偏房一處隱蔽的房頂上張望厦酬。 院中可真熱鬧,春花似錦瘫想、人聲如沸仗阅。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)减噪。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筹裕,已是汗流浹背醋闭。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留朝卒,地道東北人证逻。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像扎运,于是被迫代替她去往敵國(guó)和親瑟曲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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

  • lambda表達(dá)式(又被成為“閉包”或“匿名方法”)方法引用和構(gòu)造方法引用擴(kuò)展的目標(biāo)類(lèi)型和類(lèi)型推導(dǎo)接口中的默認(rèn)方法...
    183207efd207閱讀 1,473評(píng)論 0 5
  • 項(xiàng)目中經(jīng)常會(huì)遇到列表搜索查詢(xún)豪治,大部分的查詢(xún)是可以通過(guò)sql語(yǔ)句來(lái)實(shí)現(xiàn)的洞拨,有些特殊的搜索排序sql則實(shí)現(xiàn)不了,例如中...
    信徒_allen閱讀 2,577評(píng)論 0 1
  • 從三月份找實(shí)習(xí)到現(xiàn)在负拟,面了一些公司烦衣,掛了不少,但最終還是拿到小米掩浙、百度花吟、阿里、京東厨姚、新浪衅澈、CVTE、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,209評(píng)論 11 349
  • Java中提供了兩種對(duì)集合或數(shù)組中元素進(jìn)行排序的方法谬墙,一種是實(shí)現(xiàn)Comparable接口今布,另一種是實(shí)現(xiàn)Compar...
    EakonZhao閱讀 8,383評(píng)論 0 9
  • 因?yàn)樵诤酰允軅?因?yàn)閻?ài)上你拭抬,我便沒(méi)了我部默。 愛(ài)上你的那一刻,畫(huà)地為牢禁錮了我造虎,為你放下了所有傅蹂。 把你放在心尖尖...
    馨彤521閱讀 441評(píng)論 0 1