一、簡要介紹
Arrays里我們用的比較多的就是sort函數,這里我寫一點我的學習過程。
sort函數本身的排序性能是比較高的,它會在不同情況下運用不同的排序方法,如快排、二叉排,它給出了默認的從小到大的排序,同時也提供了自定義的排序方法,這里我會從基本數據類型的排序和自己創建對象進行排序來說明。(JDK版本為11)
二、基本數據類型的默認排序
1. int型
基本代碼
class sort1{
public static void main(String[] args) {
int[] help = new int[]{1,2,3,4,2,3,1,4,643,4};
Arrays.sort(help);
}}
點擊查看代碼
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
2. char型
我將上述代碼里面的類型改為char,發現調用的還是同一個方法,這個方法的參數可以有很多種,基本上默認的數據類型都可以用,剩下的數據類型我就不一一舉例。
點擊查看代碼
public static void sort(char[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
三、基本數據類型的自定義排序
Java里面的sort函數提供了一個Comparator接口使用戶能夠自定義排序順序,如果需要自己定義排序順序,需要實現一下Comparator接口,如圖所示:
可以看到sort這里可以接受兩個參數,第一個是待排序的數組,第二個是一個Comparator接口。
我們拿字符數組排序來舉例子:
點擊查看代碼
public static void main(String[] args) {
Character[] help = new Character[]{'e','b','e','x','p','c','a'};
//Arrays.sort(help);
Arrays.sort(help,new Comparator<Character>(){
@Override
public int compare(Character o1, Character o2) {
return o1-o2;
}
});
}
這是一個完整的自定義排序程序,我們使用內部類的形式實現了Comparator接口,可以看到再實現了Comparator接口中我們要實現它的compare的方法,這個方法就是我們自定義排序的關鍵,它有兩個參數,兩個char類型的封裝類型的變量,而我們的返回值是一個int型,這是為什么?
下面我們來詳細說明:
1、為什么compare的參數是兩個封裝類型?
我們這里追一下源碼:
public static <T> void sort(T[] a, Comparator<? super T> c)
這是sort方法的真實樣子,可以看見它利用范型,嚴格規定來傳入參數的類型,這里我們可以大概看出為什么要傳入對象類型,那這里我可以省略嗎?當然可以!如果省略,這里默認你傳入的是一個Object對象,代碼如下:
點擊查看代碼
Arrays.sort(help,new Comparator(){
@Override
public int compare(Object o1, Object o2) {
return (Character)o1-(Character) o2;
}
});
Character[] help = new Character[]{'e','b','e','x','p','c','a'};
2、為什么我們的返回值是兩個對象相減,這里我們繼續追原碼:
這是sort函數的全貌,可以看到,當我們傳入的數組不為0時,真正起排序作用的是TimSort.sort(),因為sort會根據傳入待排序數組的類型,長度等進行合適的選擇,還有更多的__Sort對象含有這個sort方法,而我們的例子這里調用的是TimSort里面的sort方法
點擊查看代碼
public static <T> void sort(T[] a, Comparator<? super T> c) {
if (c == null) {
sort(a);
} else {
if (LegacyMergeSort.userRequested)
legacyMergeSort(a, c);
else
TimSort.sort(a, 0, a.length, c, null, 0, 0);
}
}
我們直接進入真正行使排序功能的代碼:
點擊查看代碼
assert lo < hi;
int runHi = lo + 1;
if (runHi == hi)
return 1;
// 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++;
}
return runHi - lo;
本文不關心如何排序,我們着重看compare方法起了什么作用,注意看這兩句代碼:
if (c.compare(a[runHi++], a[lo]) < 0)
while (runHi < hi && c.compare(a[runHi], a[runHi - 1]) >= 0)
可以看到這里直接調用了我們所實現的compare方法,用來進行排序里面的判斷,這里真正的原理清楚了,原來我們返回的值可以直接影響到整個排序的進程,而返回值的正負是判斷的關鍵,所以compare的返回值返回的是一個int型:
點擊查看代碼
public int compare(Character o1, Character o2) {
return o1-o2;
}
這里雖然返回的是兩個char的封裝對象相減,但在計算的時候會自動解封裝,轉換為兩個實數相減,最后返回一個int型的變量,解封裝源碼:
點擊查看代碼
public char charValue() {
return value;
}
返回的int值的正負影響到真正的排列順序,而我們想要改變排列順序就很簡單,轉換一下o1和o2的順序即可,讓返回正數的情況返回負數。基本上自定義排序的原理就在於此,那我們是否可以自己來實現這樣的原理,當然可以!下面我用冒泡排序來演示一下。
四、自己實現自定義排序的原理(利用冒泡排序)
點擊查看代碼
public static void main(String[] args) {
int []a = new int[]{1,2,3,2,3,4,52,3};
for(int i = 0; i < a.length; i++){
for(int j = 0; j < a.length-i-1;j++){
if(compare(a[j],a[j+1]) > 0){
int tem = a[j];
a[j] = a[j+1];
a[j+1] = tem;
}
}
}
for(int i = 0;i < a.length; i++){
System.out.print(a[i]+"\t");
}
}
public static int compare(int o1,int o2){
return o1-o2;
}
可以看到我們利用了我們自己實現的compare方法對冒泡排序中的判斷條件進行了干預,造成了我們可以利用o1和o2的順序來對最后的排序結果進行干預,核心就是下面的代碼:
if(compare(a[j],a[j+1]) > 0)
public static int compare(int o1,int o2){ return o1-o2; }