您好,登錄后才能下訂單哦!
這篇文章主要介紹了Java如何使用Arrays.sort()方法實現給對象排序,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
當我們給一個整型數組或者浮點型之類的數組排序的時候,很簡單就可以達到我們排序的目的,無非是排序算法的問題。那么,如果我們現在想根據對象的一個屬性值給一個對象數組進行排序呢?
假如我們現在有一個Car類型,Car類中有一個double型的speed屬性用來描述車輛的速度,現在我們想根據車速來對一個Car數組中的車輛進行排序,怎么做呢?
public class Car{ private double speed;//車輛的速度屬性 public Car(double speed) { this.speed = speed; } public double getSpeed() { return speed; } public void setSpeed(double speed) { this.speed = speed; } }
我們可以通過比較對象的屬性值,根據比較結果,對對象數組進行排序,例如,假如我們使用的是冒泡排序的話,就需要寫成下面這樣:
for(int i=0;i<a.length-1;i++)//a為一個存儲了很多Car對象的數組 { for(int j=0;j<a.length-i-1;j++) { if(a[j].getSpeed()>a[j+1].getSpeed())//交換操作 { Car temp=a[j]; a[j]=a[j+1]; a[j+1]=temp; } } }
這樣寫確實能實現了效果,但是未必性能很好,由于是自己寫的,寫起來也會比較麻煩,下面的方法會更好
我們先來看看用Array.sort()方法實現對車輛排序的代碼:
其中,Car這個類有兩種寫法:
第一種寫法:
public class Car implements Comparable<Car>{ private double speed; public Car(double speed) { this.speed = speed; } public double getSpeed() { return speed; } public void setSpeed(double speed) { this.speed = speed; } @Override public int compareTo(Car newCar) { return Double.compare(this.getSpeed(),newCar.getSpeed()); } }
第二種寫法
public class Car implements Comparable{ private double speed; public Car(double speed) { this.speed = speed; } public double getSpeed() { return speed; } public void setSpeed(double speed) { this.speed = speed; } @Override public int compareTo(Object newCar) { return Double.compare(this.getSpeed(),((Car)newCar).getSpeed()); } }
main方法:
public class Test8 { public static void main(String[] args) { Car[] carList = new Car[3]; carList[0] = new Car(30); carList[1] = new Car(10); carList[2] = new Car(55); Arrays.sort(carList); for(Car c:carList) { System.out.println(c.getSpeed()); } } }
輸出結果:
解析:
顧名思義,Arrays類的sort()方法是對一個數組進行排序的方法,sort()方法的參數是一個對象數組,也就是要排序的那個數組,但是有些特別的是,這個對象數組中存儲的對象的類必須實現了Comparable接口。
Comparable接口是用來比較大小的,要實現這個接口,我們必須重寫接口中的CompareTo()方法,這個方法用來比較兩個對象的大小,因為方法本身并不知道我我們會根據對象的哪個屬性來比較兩個對象的大小,因此在這個方法中,我們可以定義我們自己的比較規則。
在上述的代碼中,我們調用了Double類的比較方法來比較兩個對象的speed屬性的大小,如果調用方法的Car對象的speed屬性值比方法參數傳進來的Car對象的speed值大就返回1,相等就返回0,小于就返回-1。當然,我們也可以自己手寫這個部分,無非就是if else判斷的事。
之所以有兩種寫法,是因為第一種使用了泛型,第二種使用了對象的強制轉換,可以看到,當我們使用泛型的時候,在調用對象的時候就已經確定了使用接口的時候用的是哪一種對象,因此就不用把對象類型強制轉換為我們想要的類型了。而第二種我們沒有使用泛型,因此就需要把參數傳進來的對象強制轉換成我們想要的類型然后再進行操作。推薦使用第一種寫法。
接下來,我們在main方法中構建了一個Car類型的數組,然后直接調用Arrays.sort()方法傳進數組作為參數就可以對數組進行排序了。
這種方法比較方便,快捷,在性能方面也有不錯的表現,本人親測在排序20萬個對象的時候花費差不多200ms左右的時間。
當然,這種方法主要圖的是方便,當我們需要排序大量的數據的時候,還是應該結合自己數據的特性來具體選擇一種排序方式自己寫。
首先先來看一下Arrays.sort()使用的例子。
Arrays.sort(int[] a)
//注意一定要用Integer對象類 Integer[] a1 = {34, 57, 46, 89, 98, 12, 55, 84, 29}; Integer[] a2 = {34, 57, 46, 89, 98, 12, 55, 84, 29}; //增序,Arrays.sort()默認升序 Arrays.sort(a1); System.out.println("Arrays.sort()升序:"); for (int i = 0; i < a1.length; i++) { System.out.print(a1[i] + " "); } //降序,可用Comparator()匿名內部類 Arrays.sort(a2, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2.compareTo(o1); } }); System.out.println("\nArrays.sort()降序:"); for (int i = 0; i < a2.length; i++) { System.out.print(a2[i]+ " "); }
若是基本類型,需要轉化為對應的對象類型(如:int轉化為Integer)Arrays.sort()可以排序基本對象類型,但是不可以使用基本數據類型。
Arrays.sort()默認的是升序排序,降序排序可采用Collection.sort()匿名內部類。
數組與list一樣,需要遍歷出來。
運行結果如下:
Arrays.sort()升序: 12 29 34 46 55 57 84 89 98
Arrays.sort()降序: 98 89 84 57 55 46 34 29 12
Arrays.sort(int[] a, int fromIndex, int toIndex)
//注意一定要用Integer對象類 Integer[] a1 = {34, 57, 46, 89, 98, 12, 55, 84, 29}; //對數組中的第四位到第7位(不包含第七位)(左閉右開原則)進行排序 Arrays.sort(a1,3,6); System.out.println("Arrays.sort()升序:"); for (int i = 0; i < a1.length; i++) { System.out.print(a1[i] + " "); }
運行結果如下:
結合文檔以及源代碼,我們發現,jdk中的Arrays.sort()的實現是通過所謂的雙軸快排的算法
快速排序使用的是分治思想,將原問題分成若干個子問題進行遞歸解決。選擇一個元素作為軸(pivot),通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比軸元素小,另外一部分的所有數據都比軸元素大,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。
雙軸快排(DualPivotQuicksort),顧名思義有兩個軸元素pivot1,pivot2,且pivot ≤ pivot2,將序列分成三段:x < pivot1、pivot1 ≤ x ≤ pivot2、x >pivot2,然后分別對三段進行遞歸。這個算法通常會比傳統的快排效率更高,也因此被作為Arrays.java中給基本類型的數據排序的具體實現。
下面我們以JDK1.8中Arrays對int型數組的排序為例來介紹其中使用的雙軸快排:
1.判斷數組的長度是否大于286,大于則使用歸并排序(merge sort),否則執行2。
// Use Quicksort on small arrays if (right - left < QUICKSORT_THRESHOLD) { sort(a, left, right, true); return; } // Merge sort ......
2.判斷數組長度是否小于47,小于則直接采用插入排序(insertion sort),否則執行3。
// Use insertion sort on tiny arrays if (length < INSERTION_SORT_THRESHOLD) { // Insertion sort ...... }
3.用公式length/8+length/64+1近似計算出數組長度的1/7。
// Inexpensive approximation of length / 7 int seventh = (length >> 3) + (length >> 6) + 1;
4.取5個根據經驗得出的等距點。
/* * Sort five evenly spaced elements around (and including) the * center element in the range. These elements will be used for * pivot selection as described below. The choice for spacing * these elements was empirically determined to work well on * a wide variety of inputs. */ int e3 = (left + right) >>> 1; // The midpoint int e2 = e3 - seventh; int e1 = e2 - seventh; int e4 = e3 + seventh; int e5 = e4 + seventh;
5.將這5個元素進行插入排序
// Sort these elements using insertion sort if (a[e2] < a[e1]) { int t = a[e2]; a[e2] = a[e1]; a[e1] = t; } if (a[e3] < a[e2]) { int t = a[e3]; a[e3] = a[e2]; a[e2] = t; if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; } } if (a[e4] < a[e3]) { int t = a[e4]; a[e4] = a[e3]; a[e3] = t; if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t; if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; } } } if (a[e5] < a[e4]) { int t = a[e5]; a[e5] = a[e4]; a[e4] = t; if (t < a[e3]) { a[e4] = a[e3]; a[e3] = t; if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t; if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; } } } }
6.選取a[e2],a[e4]分別作為pivot1,pivot2。由于步驟5進行了排序,所以必有pivot1 <=pivot2。定義兩個指針less和great,less從最左邊開始向右遍歷,一直找到第一個不小于pivot1的元素,great從右邊開始向左遍歷,一直找到第一個不大于pivot2的元素。
/* * Use the second and fourth of the five sorted elements as pivots. * These values are inexpensive approximations of the first and * second terciles of the array. Note that pivot1 <= pivot2. */ int pivot1 = a[e2]; int pivot2 = a[e4]; /* * The first and the last elements to be sorted are moved to the * locations formerly occupied by the pivots. When partitioning * is complete, the pivots are swapped back into their final * positions, and excluded from subsequent sorting. */ a[e2] = a[left]; a[e4] = a[right]; /* * Skip elements, which are less or greater than pivot values. */ while (a[++less] < pivot1); while (a[--great] > pivot2);
7.接著定義指針k從less-1開始向右遍歷至great,把小于pivot1的元素移動到less左邊,大于pivot2的元素移動到great右邊。這里要注意,我們已知great處的元素小于pivot2,但是它于pivot1的大小關系,還需要進行判斷,如果比pivot1還小,需要移動到到less左邊,否則只需要交換到k處。
/* * Partitioning: * * left part center part right part * +--------------------------------------------------------------+ * | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 | * +--------------------------------------------------------------+ * ^ ^ ^ * | | | * less k great * * Invariants: * * all in (left, less) < pivot1 * pivot1 <= all in [less, k) <= pivot2 * all in (great, right) > pivot2 * * Pointer k is the first index of ?-part. */ outer: for (int k = less - 1; ++k <= great; ) { int ak = a[k]; if (ak < pivot1) { // Move a[k] to left part a[k] = a[less]; /* * Here and below we use "a[i] = b; i++;" instead * of "a[i++] = b;" due to performance issue. */ a[less] = ak; ++less; } else if (ak > pivot2) { // Move a[k] to right part while (a[great] > pivot2) { if (great-- == k) { break outer; } } if (a[great] < pivot1) { // a[great] <= pivot2 a[k] = a[less]; a[less] = a[great]; ++less; } else { // pivot1 <= a[great] <= pivot2 a[k] = a[great]; } /* * Here and below we use "a[i] = b; i--;" instead * of "a[i--] = b;" due to performance issue. */ a[great] = ak; --great; } }
8.將less-1處的元素移動到隊頭,great+1處的元素移動到隊尾,并把pivot1和pivot2分別放到less-1和great+1處。
// Swap pivots into their final positions a[left] = a[less - 1]; a[less - 1] = pivot1; a[right] = a[great + 1]; a[great + 1] = pivot2;
9.至此,less左邊的元素都小于pivot1,great右邊的元素都大于pivot2,分別對兩部分進行同樣的遞歸排序。
// Sort left and right parts recursively, excluding known pivots sort(a, left, less - 2, leftmost); sort(a, great + 2, right, false);
10.對于中間的部分,如果大于4/7的數組長度,很可能是因為重復元素的存在,所以把less向右移動到第一個不等于pivot1的地方,把great向左移動到第一個不等于pivot2的地方,然后再對less和great之間的部分進行遞歸排序。
/* * If center part is too large (comprises > 4/7 of the array), * swap internal pivot values to ends. */ if (less < e1 && e5 < great) { /* * Skip elements, which are equal to pivot values. */ while (a[less] == pivot1) { ++less; } while (a[great] == pivot2) { --great; } } ...... // Sort center part recursively sort(a, less, great, false);
算法步驟
1.對于很小的數組(長度小于47),會使用插入排序。
2.選擇兩個點P1,P2作為軸心,比如我們可以使用第一個元素和最后一個元素。
3.P1必須比P2要小,否則將這兩個元素交換,現在將整個數組分為四部分:
(1)第一部分:比P1小的元素。
(2)第二部分:比P1大但是比P2小的元素。
(3)第三部分:比P2大的元素。
(4)第四部分:尚未比較的部分。
在開始比較前,除了軸點,其余元素幾乎都在第四部分,直到比較完之后第四部分沒有元素。
4.從第四部分選出一個元素a[K],與兩個軸心比較,然后放到第一二三部分中的一個。
5.移動L,K,G指向。
6.重復 4 5 步,直到第四部分沒有元素。
7.將P1與第一部分的最后一個元素交換。將P2與第三部分的第一個元素交換。
8.遞歸的將第一二三部分排序。
**總結:**Arrays.sort對升序數組、降序數組和重復數組的排序效率有了很大的提升,這里面有幾個重大的優化。
1.對于小數組來說,插入排序效率更高,每次遞歸到小于47的大小時,用插入排序代替快排,明顯提升了性能。
2.雙軸快排使用兩個pivot,每輪把數組分成3段,在沒有明顯增加比較次數的情況下巧妙地減少了遞歸次數。
3.pivot的選擇上增加了隨機性,卻沒有帶來隨機數的開銷。
4.對重復數據進行了優化處理,避免了不必要交換和遞歸。
感謝你能夠認真閱讀完這篇文章,希望小編分享的“Java如何使用Arrays.sort()方法實現給對象排序”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業資訊頻道,更多相關知識等著你來學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。