日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java comparator 降序排序_【转】java comparator 升序、降序、倒序从源码角度理解

發(fā)布時(shí)間:2025/3/20 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java comparator 降序排序_【转】java comparator 升序、降序、倒序从源码角度理解 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

原文鏈接:https://blog.csdn.net/u013066244/article/details/78997869

環(huán)境

jdk:1.7+

前言

之前我寫過關(guān)于comparator的理解,但是都理解錯(cuò)了。

java 自定義排序【Comparator升序降序的記法】

特別是 上面這篇,完全理解錯(cuò)了,排序的真正的意思。

最近通過查看源碼,打斷點(diǎn)的方式,一步步的查看、演算。算是明白了!

當(dāng)時(shí)我心里的疑惑是:

① -1到底表示不表示倒序;

② -1、0、1這三個(gè)值真的需要同時(shí)使用嗎?能不能只使用其中某個(gè)就行了。

③-1是不是就是表示不調(diào)整順序,其他都是要調(diào)整順序。

真正正確的理解:

① jdk官方默認(rèn)是升序,是基于:

< return -1

= return 0

> return 1

1

2

3

官方的源碼就是基于這個(gè)寫的;可以理解為硬性規(guī)定。

也就是說,排序是由這三個(gè)參數(shù)同時(shí)決定的。

如果要降序就必須完全相反:

< return 1

= return 0

> return -1

1

2

3

為什么呢?這個(gè)只能通過源碼的方式去看了。

測(cè)試代碼

首先,我寫了如下的測(cè)試代碼:

public static void main(String[] args) {

List re = new ArrayList<>();

re.add(1);

re.add(2);

re.add(6);

re.add(5);

re.add(8);

re.add(8);

re.add(4);

Collections.sort(re, new Comparator() {

@Override

public int compare(Integer o1, Integer o2) {

//下面這么寫,結(jié)果是降序

if(o1 < o2){

return 1;

}else if(o1 > o2){

return -1;

}

return 0;

}

});

System.out.println(re);

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

降序

開始debug測(cè)試:

第一步: 程序先調(diào)用如下方法:

@SuppressWarnings({"unchecked", "rawtypes"})

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

list.sort(c);

}

1

2

3

4

第二步: 而list.sort(c)源碼:

這里調(diào)用的是ArrayList類的方法:

@SuppressWarnings({"unchecked", "rawtypes"})

default void sort(Comparator super E> c) {

Object[] a = this.toArray();

// 主要看到這里

Arrays.sort(a, (Comparator) c);

ListIterator i = this.listIterator();

for (Object e : a) {

i.next();

i.set((E) e);

}

}

1

2

3

4

5

6

7

8

9

10

11

第三步:調(diào)用Arrays.sort(a, (Comparator) c);方法:

public static void sort(T[] a, Comparator super T> c) {

if (c == null) {

sort(a);

} else {

if (LegacyMergeSort.userRequested)

legacyMergeSort(a, c);

else

//接下來會(huì)走這個(gè)方法,上面不會(huì)走;

//未來jdk會(huì)棄用legacyMergeSort方法。

TimSort.sort(a, 0, a.length, c, null, 0, 0);

}

}

1

2

3

4

5

6

7

8

9

10

11

12

第四步:TimSort.sort(a, 0, a.length, c, null, 0, 0);這個(gè)方法很長(zhǎng),我先貼出主要核心的:

if (nRemaining < MIN_MERGE) {

int initRunLen =

//這個(gè)方法就大致決定是順序

countRunAndMakeAscending(a, lo, hi, c);

binarySort(a, lo, hi, lo + initRunLen, c);

return;

}

1

2

3

4

5

6

7

第五步:countRunAndMakeAscending方法:

private static int countRunAndMakeAscending(T[] a, int lo, int hi, Comparator super T> c) {

// lo 是數(shù)組起始位置 也就是 0

assert lo < hi;

// runHi = 1,這個(gè)值會(huì)隨著循環(huán)而改變,表示當(dāng)前元素的位置

int runHi = lo + 1;

// hi是數(shù)組長(zhǎng)度

if (runHi == hi)

return 1;

// Find end of run, and reverse range if descending

//這里c.compare()調(diào)用就是我們重寫的方法

if (c.compare(a[runHi++], a[lo]) < 0) { // Descending

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)

runHi++;

reverseRange(a, lo, runHi);

} else {

// Ascending -- 英文的注釋,默認(rèn)是升序;不用管這個(gè)注釋

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)

runHi++;

}

return runHi - lo;

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

這個(gè)方法就是關(guān)鍵;

我上面創(chuàng)建了一個(gè)數(shù)組:

1 2 6 5 8 8 4

//其中

< 1

= 0

> -1

1

2

3

4

5

if (c.compare(a[runHi++], a[lo]) < 0) — 這句代碼,對(duì)我的測(cè)試代碼而言:if (c.compare(2, 1) < 0)中c.compare(2,1)得到的就是-1。接著就是執(zhí)行:

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)

runHi++;

reverseRange(a, lo, runHi);

1

2

3

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)中c.compare(a[runHi], a[runHi - 1]) < 0)就是c.compare(6, 2) < 0),而c.compare(6, 2)返回的是-1,所以會(huì)接著循環(huán)執(zhí)行,runHi++后,此時(shí)runHi=2。就我的測(cè)試代碼就會(huì)去判斷c.compare(5, 6),其返回的是1,循環(huán)結(jié)束,接著執(zhí)行reverseRange(a, lo, runHi);。這個(gè)是個(gè)反轉(zhuǎn)方法。

效果就是:

數(shù)組:1 2 6 5 8 8 4

反轉(zhuǎn)后:6 2 1 5 8 8 4

1

2

可以看出,前面三個(gè)數(shù)字順序已經(jīng)好了,后面的5 8 8 4,會(huì)在執(zhí)行binarySort(a, lo, hi, lo + initRunLen, c);這個(gè)方法時(shí)來進(jìn)行二分插入排序。

第六步:執(zhí)行binarySort(a, lo, hi, lo + initRunLen, c);方法:

private static void binarySort(T[] a, int lo, int hi, int start,

Comparator super T> c) {

assert lo <= start && start <= hi;

if (start == lo)

start++;

for ( ; start < hi; start++) {

T pivot = a[start];

// Set left (and right) to the index where a[start] (pivot) belongs

int left = lo;

int right = start;

assert left <= right;

/*

* Invariants:

* pivot >= all in [lo, left).

* pivot < all in [right, start).

*/

//這個(gè)是關(guān)鍵地方

while (left < right) {

//這里相當(dāng)于除以2

int mid = (left + right) >>> 1;

if (c.compare(pivot, a[mid]) < 0)

right = mid;

else

left = mid + 1;

}

//當(dāng)left等于right時(shí),就說明找到位置了。

//assert是斷言,要是為false會(huì)直接報(bào)錯(cuò)

assert left == right;

/*

* The invariants still hold: pivot >= all in [lo, left) and

* pivot < all in [left, start), so pivot belongs at left. Note

* that if there are elements equal to pivot, left points to the

* first slot after them -- that's why this sort is stable.

* Slide elements over to make room for pivot.

*/

int n = start - left; // The number of elements to move

// Switch is just an optimization for arraycopy in default case

switch (n) {

case 2: a[left + 2] = a[left + 1];

case 1: a[left + 1] = a[left];

break;

//要是移動(dòng)的位數(shù)大于2,就執(zhí)行如下方法;

default: System.arraycopy(a, left, a, left + 1, n);

}

a[left] = pivot;

}

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

例子中的數(shù)組:

6 2 1 5 8 8 4

//循環(huán)執(zhí)行binarySort方法后,

//會(huì)依次把 5 8 8 4 插入到相應(yīng)的位置

//最終的結(jié)果為:

// 8 8 6 5 4 2 1

1

2

3

4

5

升序

這是,jdk默認(rèn)的順序,例子:

< -1 > 1 =0

1 2 6 5 8 8 4

1

2

執(zhí)行步驟和上面降序是一樣的,我就直接分析核心部分了:

// Find end of run, and reverse range if descending

if (c.compare(a[runHi++], a[lo]) < 0) { // Descending

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)

runHi++;

reverseRange(a, lo, runHi);

} else { // Ascending

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)

runHi++;

}

1

2

3

4

5

6

7

8

9

當(dāng)執(zhí)行到這里時(shí),c.compare(a[runHi++], a[lo]) < 0就是c.compare(2, 1) < 0,而`c.compare(2, 1)返回的是1,那么程序就會(huì)進(jìn)入else的部分:

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)

runHi++;

1

2

代碼c.compare(a[runHi], a[runHi - 1])就是c.compare(6, 2)返回的是1符合條件(大于0),

runHi++,此時(shí)runHi=3。c.compare(a[runHi], a[runHi - 1])就是c.compare(5, 6),其返回的是-1,不符合條件。循環(huán)結(jié)束,數(shù)組結(jié)果為:

//可以看出什么都沒有變

1 2 6 5 8 8 4

//但是方法的`return runHi - lo;`這個(gè)返回的結(jié)果就是3

//這個(gè)返回值,會(huì)在`binarySort(a, lo, hi, lo + initRunLen, c);`中用到。

1

2

3

4

下一步:執(zhí)行binarySort(a, lo, hi, lo + initRunLen, c);其中initRunLen = 3;

在執(zhí)行二分插入時(shí),就會(huì)從數(shù)組下標(biāo)為3開始;

1 2 6 5 8 8 4

//從下標(biāo)為3,開始二分插入排序;即從5開始。

1 2 5 6 8 8 4

接著是8

1 2 5 6 8 8 4

接著是第二個(gè)8

1 2 5 6 8 8 4

接著是4

1 2 4 5 6 8 8

1

2

3

4

5

6

7

8

9

通過升序和降序,我們基本可以知道排序步驟:

①countRunAndMakeAscending這個(gè)方法確定是順序還是降序,并且將數(shù)組的一部分排列好。并返回未排列的起始位置

②將未排列的起始位置傳遞給binarySort進(jìn)行二分插入排序。

倒序

我們先來看看倒序的結(jié)果:

1 2 6 5 8 8 4

倒序后:

4 8 8 5 6 2 1

//怎么做到呢?

//不管大于、小于和等于 都返回 -1

1

2

3

4

5

從源碼上看countRunAndMakeAscending方法:

f (c.compare(a[runHi++], a[lo]) < 0) { // Descending

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)

runHi++;

reverseRange(a, lo, runHi);

} else { // Ascending

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)

runHi++;

}

1

2

3

4

5

6

7

8

c.compare()得到的永遠(yuǎn)都是-1,所以其會(huì)將下面這段代碼執(zhí)行完畢:

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)

runHi++;

1

2

循環(huán)完畢后,此時(shí)runHi就是數(shù)組的長(zhǎng)度7。

接著執(zhí)行reverseRange(a, lo, runHi);,將整個(gè)數(shù)組進(jìn)行倒序。

該方法完全執(zhí)行完成后,返回值就是數(shù)組長(zhǎng)度。

此時(shí)再執(zhí)行binarySort方法時(shí),for ( ; start < hi; start++)中的start是剛剛傳進(jìn)來的值,也就是數(shù)組長(zhǎng)度,而hi也是數(shù)組長(zhǎng)度,所以二分插入方法什么都沒有做,只是調(diào)用了下。

0 到底是什么作用

假設(shè)不管大于、小于、等于,我們都返回0 ,會(huì)發(fā)現(xiàn)順序沒有變;而且你會(huì)發(fā)現(xiàn),要是都返回1的話,順序也是沒有變的!

從countRunAndMakeAscending方法中可以得出結(jié)論:

// Find end of run, and reverse range if descending

if (c.compare(a[runHi++], a[lo]) < 0) { // Descending

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) < 0)

runHi++;

reverseRange(a, lo, runHi);

} else {

//走這個(gè)循環(huán)

while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)

runHi++;

}

1

2

3

4

5

6

7

8

9

10

當(dāng)不管大于、小于、等于時(shí),我們都返回一個(gè)值時(shí),0和1效果是一樣的,就是不排序;-1就是倒序。

可以要是 是如下寫法:

public int compare(Integer o1, Integer o2) {

if(o1 < o2){

return 1;

}/*else if(o1 > o2){

return 1;

}*/

return -1;

}

1

2

3

4

5

6

7

8

也就是 我們把等于和大于都返回-1,小于返回1。發(fā)現(xiàn)也是可以降序的,或者反過來,就是升序。視乎覺得0好像是多余的。

其實(shí)0表示的是,相同元素不排序,要是我們把等于返回為-1,那么兩個(gè)相同的元素會(huì)交互順序;

1 2 6 5 8 8 4

//也就是這里面兩個(gè)8 會(huì)交換順序

1

2

對(duì)數(shù)字而言交換順序沒有關(guān)系,但是里面要是是Map對(duì)象的話,那就有關(guān)系,因?yàn)橛袝r(shí)我們是希望相同元素不進(jìn)行順序調(diào)整的。

要是我們把等于返回為1效果和0是一樣的都是不排序。

總結(jié)

排序其實(shí)是由三個(gè)數(shù)字同時(shí)決定的;

升序(默認(rèn),即官方定義,畢竟代碼實(shí)現(xiàn)就是基于這個(gè)寫的):

< -1

= 0 //或者 1效果是一樣的;-1相同元素會(huì)發(fā)生位置調(diào)整

> 1

1

2

3

降序:

< 1

= 0 //或者 1效果是一樣的;-1相同元素會(huì)發(fā)生順序調(diào)整

> -1

1

2

3

倒序:

//直接

return -1;

1

2

不改變順序:

//直接

return 0或者1;

1

2

底層做法是:先確定局部順序,再利用二分查找法,進(jìn)行后續(xù)排序:

數(shù)組:1 2 6 5 8 8 4

反轉(zhuǎn)后:6 2 1 5 8 8 4

1

2

這里先確定了6 2 1的順序,后面5 8 8 4的位置就是利用二分查找法來確定的!

---------------------

作者:山鬼謠me

來源:CSDN

原文:https://blog.csdn.net/u013066244/article/details/78997869

版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請(qǐng)附上博文鏈接!

總結(jié)

以上是生活随笔為你收集整理的java comparator 降序排序_【转】java comparator 升序、降序、倒序从源码角度理解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 日本裸体视频 | 精品国产18久久久久久二百 | 欧美破处大片 | 在线看你懂得 | 国产av成人一区二区三区高清 | 18黄暴禁片在线观看 | 中文字幕日韩精品在线 | 欧美三级午夜理伦 | 国内偷拍久久 | 夜色一区| 国产91黄色 | 禁断介护av一区二区 | 蜜桃久久久久 | 黄网站免费大全入口 | 亚洲一区二区三区四区五区xx | 麻豆伊甸园 | 日韩激情视频网站 | 四虎精品在线 | 极品粉嫩国产18尤物 | 夜色福利| 国产又粗又大又爽视频 | 手机av网站| 国产成人精品视频一区二区 | 国产亚洲精品美女久久久 | 久久香蕉网站 | 亚州av | 丰满少妇熟乱xxxxx视频 | 亚洲天堂999| 日韩高清一区 | 四虎成人网| 国内av在线播放 | 欧美大片大全 | 春日野结衣av | 又黄又湿的网站 | 日韩精品导航 | 日韩精选视频 | 久久av在线 | 九九人人 | 精品国产视频一区二区 | 国产日比视频 | 成人午夜激情视频 | 四虎国产在线 | 一区二区三区欧美 | 亚洲高清精品视频 | 免费成人在线观看 | www.com国产 | 日本中文字幕在线免费观看 | 欧美成人精品欧美一级乱 | 中文字幕在线精品 | 国产精品一级片在线观看 | 高清免费av | 久久久高潮| 国产一区二区三区在线免费 | 久久五月综合 | 成人在线免费电影 | 四虎国产 | 国产精品99久久久久久一二区 | www.伊人.com | 成人观看网站 | 男人晚上看的视频 | 精品一区二区三区四区视频 | 久久久久久久国产精品毛片 | 国产乱淫av片免费 | 亚洲国产一区二区在线观看 | 精品人妻一区二 | 日韩欧美中文字幕一区二区三区 | 男女插插视频 | 国产高清无遮挡 | av国产一区二区 | 手机在线亚洲 | 亚洲黄色小说网 | 摸大乳喷奶水www视频 | 国产在线久久久 | 色哟哟无码精品一区二区三区 | 久久婷婷五月国产色综合激情 | 在线中文字幕亚洲 | 天天鲁一鲁摸一摸爽一爽 | 亚洲欧美日韩在线不卡 | 亚洲综合一区中 | 成人久久精品 | 神马久久精品 | 日韩不卡在线视频 | 一级视频在线免费观看 | 国产三级国产精品国产专区50 | 不用播放器的av网站 | 亚洲三级在线看 | 黑人巨大av | 亚洲av综合av一区二区三区 | 精品一区二区三区不卡 | 高潮网址 | 国产精品视频在线播放 | 日韩三级一区二区三区 | 欧美大片免费观看网址 | 国产制服91一区二区三区制服 | 毛片日本 | 五月依人网| 国产一区二区三区精品在线观看 | a天堂在线视频 | 成人合集 |