java arrays.sort() c_5.4 (Java学习笔记)集合的排序(Collections.sort(),及Arrays.sort())...
1.Comparable接口
這個接口顧名思義就是用于排序的,如果要對某些對象進行排序,那么該對象所在的類必須實現
Comparabld接口。Comparable接口只有一個方法CompareTo(),這個方法可以看做是指定的排序規則。
內置類已經實現了CompareTo方法,例如long
小于返回-1,等于返回0,大于返回1.
這里只舉一個例子,例如int,double,Date等可以排序的內置類都已經實現了CompareTo方法,即指定了排序規則。
2.Collections.sort()和Arrays.sort()方法。
Colections是對集合進行操作的工具類,Collection.sort是對集合排序的方法。
Collections.sort()的底層實質是是調用Arrays.sort();
我們來看下Collections.sort()的源碼:
首先傳遞進去的集合必須是Comparable接口及其子類,而且后續會用到Comparable接口中的CompareTo方法。所以進行排序的類必須實現Comparable,
可以看出如果沒有實現Comparable接口,參數傳遞會出現錯誤。
我們點進list.sort()方法,就會看到下面代碼:
我們可以看到,它是先將集合轉換成數組,然后調用Arrays.sort()方法。
排序排完后又將數組中元素逐個放入集合。
我們接下點進Arrays.sort()方法的源碼:
參數有兩個,有一個之前轉換成數組的集合a,c是一種排序規則,后面會講到。沒有指定的話,會自動設置為null。
然后判斷是否有排序規則,有就調用使用排序規則的排序方法。這里看出當設置了排序規則c時,會進入使用排序規則的比較方法進行比較,
此時compareTo()是不起作用的,起作用的是排序規則c中的compare()方法。
接著進入sort().
我們可以看到這里有兩個排序算法,使用LegacyMergeSort.userRequested控制,第一個是歸并排序,也就是LegacyMergeSort.userRequested為true時進入的語句,
注意默認狀態下LegcyMergeSort是false,所以默認進去的是ComparableTimSort.sort()。這時一種優化的排序算法,有興趣可以去查詢下,當然這種算法比較復雜,
不便于理解。legactMergeSort()便于理解些,我們就先分析這個排序算法。
默認是進入ComparableTimSort(),要想進入legacyMergeSort()我們就需要設置下LegacyMergeSort.userRequested的值。
這時我們可以自己設置LegacyMergeSort.userRequested的值,這句話是加在main中Sort調用之前的。
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
設置為true后我們進入的就是legcyMergeSort();
/*注意:
這里只是對排序算法的一種優化,排序時的規則是不變的的。
(例如我現在要進行升序排序,可以用很多算法,但是最后的結果一定是升序的。)
這里我們主要看CompareTo()方法返回值(1,0,-1)在排序中起到的作用,不關心具體算法的優化,有興趣的可以自行查閱相關算法。*/
那么我們這時就會進入gacyMergeSort();
我們點擊該方法,看它的代碼:
首先將存放集合數組的數組克隆,然后將克隆后的數組作為參數傳遞進aux,這里c為null進入第一個mergeSort();
進入mergeSort()我們可以看到:
我們先看傳遞進去的參數,srt,dest都是存放數據的數組,str是克隆的數組,dest是原數組,low=0,high=數組長度,off = 0;
首先定義了一個legth = high - low,得到的其實也是數組長度,
一開始進行一個判斷,長度小于INSERTIONSORT_THRESHOLD(常量7)就采用更適合長度小于7的數組的插入排序算法,
我們可以看到代碼中排序的依據是CompareTo()的返回值,如果返回值大于0則交換位置,反之則不交換。
如果不小于則采用適合數組長度大于7的其他排序方法。
后面還有更適合數組長度大于7的算法,這里沒有貼出這部分代碼,有興趣可自己去了解下。
(這里只是對排序算法執行次數的一種優化,排序時的規則是不變的的。我們這里主要看CompareTo方法起到作用,不關心排序算法的優化。)
這里我們舉一個簡單的例子,假設現在我在鏈表里面存放了三個long型數據312;
我們從調用Collections.Sort()方法開始分析源碼執行的過程,理解CompareTo方法的作用。
進入mergeSort()方法后,我們先來看下傳遞進來的參數:
dest[] = {3,1,2}, low = 0, high = 3(數組長度);
接下來分析代碼執行過程:
首先i=0,然后j = 0,不滿足條件直接跳出,然后i++
此時i = 1,j=1,此時將元素轉換為Comparable接口調用CompareTo方法根據之前看的long里面的Compare方法(Comparable)dest[0].compareTo(dest[1]) = 1.
如果沒有實現Comparable接口,這里轉型也會出現錯誤。
//調用ComparTo()返回值不清楚的,回到前面去看long里面CompareTo方法
交換dest[0],dest[1]位置,此時數組順序:132.,然后j--,不滿足循環條件直接跳出,執行i++。
此時i=2,j=2,(Comparable)dest[1].compareTo(dest[2]) = 1,交換dest[1],dest[2],此時數組順序:123.執行i++;
i=3,不滿足循環條件,退出執行retun,結束排序,最終數組順序:123;
大家可以自己結合CompareTo方法的返回值分析分析排序過程。
了解了CompareTo在排序中起到的作用后,平常我們在實現Comparable接口,
實現其中的CompareTo方法時我們就可以明確CompareTo的返回值。
例如,a
如果是a>b return 1,就變成升序。分析源碼后我們可以自己分析排序到是升序還是降序。
我們也可以這樣理解,如果返回1就代表交換這兩個元素位置。
//一般情況下大于返回1,等于返回0,小于返回-1,更符合我們的思維習慣,這時排序是升序
建議一般情況下這樣寫,此時是升序,如果是要求降序排列,改變返回值,將a>b return -1就是降序了。
再次聲明,要使用排序方法,必須實現Comparable接口。
我們來舉一個具體的例子。例如新聞,我們首先要看時間降序(即最近發生的新聞出現在最前面),
其次按點擊量降序(即點擊量多的出現在前面)。
那么我們結合CompareTo方法在排序時的作用來考慮時間和點擊量比較的返回值。
首先,最近發生的新聞排在前面,如果A新聞時間在B新聞時間后面(也就是A新聞發生的時間離我們最近),
越在后面的時間的數值越大,Date本質上也是一個long型數。那么最近發生的排在前面應該降序。也就是if(a時間> b時間)return -1;
如果時間相同,我們再來考慮點擊量,點擊量大的排在前面。
那么這也是一個降序,if(點擊量a > 點擊量b) return -1;
這里也可以用之前如果返回1,就代表交換位置,-1就不交換位置這樣來理解,理解的方式有很多種,
但歸根結底都是對源碼執行過程的分析,把握這一點就可以以不變應萬變。
接下來我們來看具體的代碼。
NewS類代碼
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
public class News implements Comparable{
private String newsName;
private Date date;
private int click;
private String timeS;
public News(){}
public News(String newsName, Date date, int click){
setNewsName(newsName);
setDate(date);
setClick(click);
}
public String getNewsName() {
return newsName;
}
public void setNewsName(String newsName) {
this.newsName = newsName;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
DateFormat china_time = new SimpleDateFormat("yyyy-MM-dd hh-mm-ss");
timeS = china_time.format(date);
}
public int getClick() {
return click;
}
public void setClick(int click) {
this.click = click;
}
public int compareTo(News o) {
// TODO Auto-generated method stub
if(this.getDate().after(o.getDate()))//這句話就是如果this.getDate()的值 > o.getDate(),就降序排列。
return -1;?????????????????????? //也可以理解為如果this.getDate()的值 >o.getDate(),則不交換位置,即降序。
if(this.getClick() > o.click)//如果this的點擊數大于o的點擊數則降序排列,即點擊量大的排在前面
return -1;?????????????? //如果this的點擊量大于o的點擊量,則不交換位置,即降序排列
return 1;
}
public String toString(){
return "標題:" + this.newsName + "時間" + timeS
+ "點擊量" + this.click + "\n";
}
}
運行代碼
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
public class TestCompare {
public static void main(String[] args) {
List newsList = new LinkedList<>();
newsList.add(new News("國慶高峰堵車,各景點爆滿!",new Date(),1000));//設置為當前時間
newsList.add(new News("國慶仍有大部分家里蹲!",new Date(System.currentTimeMillis()-60*60*1000),100));//設置為當前時間前一小時
newsList.add(new News("驚呆,游客竟在做這種事!!!",new Date(),10000));//設置為當前時間,點擊量10000
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");//設置為true進入legacySort()
System.out.println(newsList);
Collections.sort(newsList);
System.out.println(newsList);
}
}
運行結果://排序前新聞
[標題:國慶高峰堵車,各景點爆滿!時間2018-10-07 09-23-53點擊量1000
, 標題:國慶仍有大部分家里蹲!時間2018-10-07 08-23-54點擊量100
, 標題:驚呆,游客竟在做這種事!!!時間2018-10-07 09-23-54點擊量10000
]//排序后新聞
[標題:驚呆,游客竟在做這種事!!!時間2018-10-07 09-23-54點擊量10000
, 標題:國慶高峰堵車,各景點爆滿!時間2018-10-07 09-23-53點擊量1000
, 標題:國慶仍有大部分家里蹲!時間2018-10-07 08-23-54點擊量100
]
我們可以看到,首先按時間(距離我們最近發生的新聞排在最前面)進行排序,時間相同按點擊量排序。
我們可以看出mergeSort中使用Compareto方法只是判斷是否大于0,
當我們的CompareTo()返回的是0或-1時,for()語句中判斷都是false,那么我們能否在返回時不要-1,只返回0,1呢?
在當前方法(LeacySort()中的mergeSort())顯然是可以的,因為0 > 0,-1>0的結果是相同的。
你要確定你進去的是LeacySort()中的mergeSort(),最好設置如下語句:
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
如果沒有設置的話LegcyMergeSort.userRequested默認為false,進去的就是另外一種排序算法,在另外一種算法中這樣設置是不行的。
當默認為false時,我們進入另外一個算法看下:(這個算法只是簡單點的過一遍,不分析具體步驟)
為false時進去的是ComparebleTimSort.sort();我們進入這個算法看下:
首先一個斷言,一般情況下都沒有問題,
然后nRemaing = hi - lo,hi是數組長度,lo是0.
MIN_MERGE是32。
接著就是根據數組a計算initRunLen的值,
我們先進入countRunAndMakeAscending();
但我們將-1也設置為0時,else后面的語句判斷的是返回值>=0。-1和0去判斷得到的結果是不同的。
這里會造成這個值有問題,而這個值需要傳遞到排序算法中,就會導致排序出現問題。News中的CompareTo方法的
返回值只有0,1的話這里的runHi - lo是3,如果是按照正常的1,0,-1來的話runHi - lo是2。這個可以結合源碼自己分析一下。
這個執行完之后就要將得到的值,傳入我們的排序算法中開始排序了。(上述分析都是基于之前代碼中創建的三個新聞對象來分析的)
返回runHi-lo后,我們回到上一級
我們將值傳入binarSort()中,然后點擊binarySort:
這個具體過程就不一一分析了,有興趣的可以分析下這些算法。
start就是之前的返回值(runHi - lo),如有只有1,0返回的是3,如果是正常的-1,0,1返回的是2.
我們來看第一個for語句,如果start是3就直接跳出循環(hi是數組長度,這里為3)不進行排序,原有數據不會發生變化。
如果是2,進行排序,最后輸出是按照規則排序好的數據。
大家可以試下修改上面代碼中Nwes的compareTo()方法的返回值和運行代碼中設置true,false的部分:
(1)將 return -1改成return 0,LegacyMergeSort.userRequested為false時,輸出未正常排序結果。
LegacyMergeSort.userRequested為true時,輸出正常排序結果。
結合上述內容理解下整個的過程。
無論是哪種排序方法,嚴格按照(1,0,-1)返回,都可以輸出對應邏輯排序結果。
結合例子、源碼與上述內容分析分析,就可以清楚的知道排序執行的流程以及compareTo()返回值的作用。
3.Comparator接口
Comparator和Comparable類似,底層實現是一樣的,理解了Comparable中CompareTo()方法的作用后
理解Comparator就很簡單了。
我們之前說的實現Comparable接口的CompareTo方法是寫在需要進行排序的類里面的,可以說這種方法就從屬于這個類。
但Comparator就不一樣了,它只是制定了一種排序規則,不需要從屬于誰,誰需要用這個規則拿來用就可以了。
具體的操作是,新建一個規則類,例如我要按照新聞點擊量排序,那么我可以把這個類命名為NwesClickSort
這個類需要實現Comparator接口,并且實現里面的ompare()方法。
我們來看下Comparator接口中compare方法:
這個作用和compareTo()方法的作用一樣,也是比較兩個值(o1,o2),然后返回1,0,-1.
使用compare排序時,就是調用這個方法來進行排序。
使用時,我們需要為NewsClickSort這個類實例化一個對象,這個對象代表一種排序規則。
然后將集合和對象(排序規則)傳遞到Collections.sort()中。
我們先來看個例子://這里面我們指定了一種排序規則,按照點擊量來排序,Nwes類還是之前的代碼,沒有任何修改。
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
public class TestCompare {
public static void main(String[] args) {
List newsList = new LinkedList<>();
newsList.add(new News("國慶高峰堵車,各景點爆滿!",new Date(),1000));//設置為當前時間
newsList.add(new News("國慶仍有大部分家里蹲!",new Date(System.currentTimeMillis()-60*60*1000),100));//設置為當前時間前一小時
newsList.add(new News("驚呆,游客竟在做這種事!!!",new Date(),10000));//設置為當前時間,點擊量10000
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");//設置為true進入legacySort()
System.out.println(newsList);
Collections.sort(newsList,new NewsClickSort());//傳遞進去的集合,和排序規則
System.out.println(newsList);
}
}
class NewsClickSort implements Comparator{
@Override
public int compare(News o1, News o2) {
if(o1.getClick() < o2.getClick()){
return 1;//這里寫得不是很規范,最好是加上等于返回0,最后小于返回-1.
}else{
return -1;
}
}
}
運行結果:
//排序前結果[標題:國慶高峰堵車,各景點爆滿!時間2018-10-10 08-40-50點擊量1000
, 標題:國慶仍有大部分家里蹲!時間2018-10-10 07-40-50點擊量100
, 標題:驚呆,游客竟在做這種事!!!時間2018-10-10 08-40-50點擊量10000
]//排序后結果
[標題:驚呆,游客竟在做這種事!!!時間2018-10-10 08-40-50點擊量10000
, 標題:國慶高峰堵車,各景點爆滿!時間2018-10-10 08-40-50點擊量1000
, 標題:國慶仍有大部分家里蹲!時間2018-10-10 07-40-50點擊量100
]
排序規則中只指定了點擊量,所以排序后的結果是按點擊量來排序的。
我們可以看到上面代碼也設置了
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");//
我們就來分析下使用comparator接口中的compare方法的執行過程。
第一步當然是點進main中的
Collections.sort
注意那個c,就是實例化的比較規則。
我們繼續點擊sort():
我們會發現和之前看Comparable接口排序的執行過程是一樣的。
只是這時候c是實例化的排序規則,不是null了。
繼續點擊sort()
這時c不等于null,所以進入后面的if(LegcyMergeSort.userRequested)進行判斷
前面說了默認LegayMergeSort.userRequested是false,這里我們設置了為true,
所以我們點進legacyMergeSort():
c不等于null,我們點進第二個分支:
我們會發現和之前sort排序過程及CompareTo()的實現思路是一樣的,只是一個調用的是compareTo方法,
一個調用的是c.compare()方法。
LegayMergeSort.userRequested為false時,也和之前分析的sort排序過程及compareTo()的實現思路是一樣的,區別只是一個調用
的是compareTo()方法,一個調用的是c.compare()方法。
LegayMergeSort.userRequested為false時的后續執行過程有興趣可以去看下,會發現和前面講的一樣。
到這里整個比較過程就很清楚了。
總結
以上是生活随笔為你收集整理的java arrays.sort() c_5.4 (Java学习笔记)集合的排序(Collections.sort(),及Arrays.sort())...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux环境下java开发_Linux
- 下一篇: java左右三角_java打印一个顺序与