Java中Arrays的sort排序原理


一、簡要介紹
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);
    }}
這個的排序結果就是默認的從小到大排序,我們追sort的原碼:
點擊查看代碼
public static void sort(int[] a) {
        DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
    }
發現它其實就是調用了一個sort(int[]a),其中調用了DualPivotQuicksort的sort方法,這個靜態類的sort方法其實是很多種排序方法的一個綜合,由於我是初學,這里不講解,可以參考https://blog.csdn.net/lyj1597374034/article/details/106720629 這篇文章,具體意思就是DualPivotQuicksort**會根據不同的場景合適的調用不同的排序方法**

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;
            }
        });
可以看到我這里沒有指定范型,o1和o2兩個對象就是Objiect類型,則在返回的時候需要進行強制類型轉換。因此在我們排序不同類型的序列時,應該傳入對應的封裝類型或者對象類型。這是自定義排序的基本條件,同時定義數組的時候也需要講定義類型改變為封裝類型,如:

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; }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM