1. 程式人生 > >JDK1.8源碼泛讀之Arrays

JDK1.8源碼泛讀之Arrays

示例 分布 IT operation list() 1.8 pos prefix sde

jdk1.8包含的常用集合工具類,一般包括兩個:
數組工具類:`java.util.Arrays `
結合工具類:`java.util.Collections`

今天就結合源碼對`java.util.Arrays `的功能進行總結說明。

1、排序方法 `sort`


  • 8種基本類型數組排序

方法直接調用`java.util.DualPivotQuicksort`(雙軸快速排序類)的相關方法進行排序。
而在該類的內部,又根據數組的大小和有序性的好壞,選擇適合的排序算法。比如數組小於指定閾值,則采用使用雙軸快排,如果順序連續性好,直接使用TimSort算法,順序連續性不好的數組直接使用了 雙軸快排 + 成對插入排序。具體過程本文不敘述。可以參考:

雙軸排序

  • object[]數組排序

對對象類型的數組的排序,其過程又有別於基本類型,jdk會借助於參數:LegacyMergeSort.userRequested
進行排序算法的選擇,如果開啟,那麽采用傳統的歸並排序,否則采用[TimSort]的算法。

  • 並行排序方法`parallelSort`

parallelsort會把array分成不同的子數組,每個子數組用sort進行排序,最後再合並排序;整個過程用ForkJoin
common pool(java.util.concurrent.ForkJoinPool)進行並發操作

2、並行累積方法`parallelPrefix`


使用提供的函數並行累積給定數組中的每個元素。例如,如果數組最初保存[2, 1, 0, 3]並且操作執行加法,則返回時數組成立[2, 3, 3, 6]。並行前綴計算通常比大型數組的順序循環更有效。

技術分享圖片
    public static int[] intArrays ={55,45,23,11,10,88,102,99};

    public static void parallelPrefix(){

        System.out.println("*******調用前*******");
        System.out.println(Arrays.toString(intArrays));

        Arrays.parallelPrefix(intArrays,(x,y)->(x+y));

        System.out.println("*******調用後*******");
        System.out.println(Arrays.toString(intArrays));
    }
View Code
1 *******排序前*******
2 [55, 45, 23, 11, 10, 88, 102, 99]
3 *******排序後*******
4 [55, 100, 123, 134, 144, 232, 334, 433]
5 55=55
6 100=55+45
7 123=55+45+23
8 ...
9 433=55+...+99

3、二叉查找`binarySearch`


在已排序數組中,找出指定值的元素所在的位置(如果為正數,返回從0開始的index,如果為負數,其絕對值-1為所找key最近的數組元素)。

 1     private static int binarySearch0(int[] a, int fromIndex, int toIndex,
 2                                      int key) {
 3         int low = fromIndex;
 4         int high = toIndex - 1;
 5 
 6         while (low <= high) {
 7             int mid = (low + high) >>> 1;
 8             int midVal = a[mid];
 9 
10             if (midVal < key)
11                 low = mid + 1;
12             else if (midVal > key)
13                 high = mid - 1;
14             else
15                 return mid; // key found
16         }
17         return -(low + 1);  // key not found.
18     }

4、相等性判斷`equals`


數組等於比較,過程相對簡單:

 1     public static boolean equals(int[] a, int[] a2) {
 2 //同一個對象,即引用地址一樣
 3         if (a==a2)
 4             return true;
 5 //一個為null
 6         if (a==null || a2==null)
 7             return false;
 8 //長度不等,直接返回
 9         int length = a.length;
10         if (a2.length != length)
11             return false;
12 //每個元素比較,有不同就返回
13         for (int i=0; i<length; i++)
14             if (a[i] != a2[i])
15                 return false;
16 
17         return true;
18     }

5、批量賦值`fill`


填充數據指定或者全部位置的值為指定值,相對簡單。

1     public static void fill(long[] a, long val) {
2         for (int i = 0, len = a.length; i < len; i++)
3             a[i] = val;
4     }

6、復制`copyOf`


對數組元素進行復制,其中返回的是一個新的數組,但是數組元素還是對原先數組堆對象的引用(即元素淺復制)。
該方法底層調用的是native方法,進行快速復制:`system.arraycopy`

 1 //測試示例
 2         ArraysDemo[] demos ={new ArraysDemo("a"),new ArraysDemo("b"),new ArraysDemo("c"),new ArraysDemo("d"),new ArraysDemo("e")};
 3 
 4         ArraysDemo[] newDemos = Arrays.copyOf(demos,demos.length);
 5 
 6         //數組對象深復制 返回false
 7         System.out.println(demos==newDemos);
 8         //數組元素淺復制,元素引用的還是同一個地址,返回true
 9         System.out.println(demos[1]==newDemos[1]);
10 
11         newDemos[0] = new ArraysDemo("f");
12         //地址改變 返回false
13         System.out.println(demos[0]==newDemos[0]);
14         //元素內容改變
15         newDemos[1].setClassVer("bb");
16         //原始對象同時改變 打印bb
17         System.out.println(demos[1].getClassVer());

7、範圍復制`copyOfRange`


數組的局部復制,最終調用6的copyOf方法。

8、返回集合`asList`


返回Arrays內部類ArrayList<>對象

1 private static class ArrayList<E> extends AbstractList<E> 2 implements RandomAccess, java.io.Serializable

相比較於java.util.ArrayList,缺少List接口的相關方法。

1 ``` 2 public class ArrayList<E> extends AbstractList<E> 3 implements List<E>, RandomAccess, Cloneable, java.io.Serializable

所以List接口的相關方法無法調用。正如《阿裏巴巴java開發規約》所描述的:

使用工具類Arrays.asList()把數組轉換成集合時,不能使用其修改集合相關的方法,它的add/remove/clear方法會拋出UnsupportedOperationException異常。
asList的返回對象是一個Arrays內部類,並沒有實現集合的修改方法,只是轉換接口,後臺數據仍舊是數組。

1     public static <T> List<T> asList(T... a) {
2         return new ArrayList<>(a);
3     }
4 
5     //測試,異常 UnsupportedOperationException
6     List list = Arrays.asList(intArrays);
7     list.add(2);

其實asList內部是調用了抽閑類AbstractList的add方法,但內部類並沒有重載實現。
所以:

1     public void add(int index, E element) {
2         throw new UnsupportedOperationException();
3     }

9、hash計算`hashCode`


獲取數組的hashcode,其內部是數組元素的hashcode的拼裝,根據類型不同又有不同的拼裝方式。

10、並行遍歷叠代器`spliterator`


Spliterator可以理解為Iterator的Split版本(但用途要豐富很多)。使用Iterator的時候,我們可以順序地遍歷容器中的元素,使用Spliterator的時候,我們可以將元素分割成多份,分別交於不於的線程去遍歷,以提高效率。使用 Spliterator 每次可以處理某個元素集合中的一個元素 — 不是從 Spliterator 中獲取元素,而是使用 tryAdvance() 或 forEachRemaining() 方法對元素應用操作。但Spliterator 還可以用於估計其中保存的元素數量,而且還可以像細胞分裂一樣變為一分為二。這些新增加的能力讓流並行處理代碼可以很方便地將工作分布到多個可用線程上完成。

11、流式處理`stream`


 1     public static void stream(){
 2         String [] strArray = new String[] {"a", "b", "c","d"};
 3 
 4         //過濾操作,過濾掉為a的字符串
 5         List afterFilterLists =Arrays.stream(strArray).filter(s -> !s.equals("a"))
 6                 .collect(Collectors.toList());
 7 
 8         System.out.println(afterFilterLists);
 9         //輸出[b, c, d]
10 
11         //foreach操作 輸出 a b c d
12         Arrays.stream(strArray).forEach(s-> System.out.println(s));
13 
14     }

JDK1.8源碼泛讀之Arrays