JDK1.8源碼(四)——java.util.Arrays 類
java.util.Arrays 類是 JDK 提供的一個工具類,用來處理數組的各種方法,而且每個方法基本上都是靜态方法,能直接通過類名Arrays調用。
1、asList
public static <T> List<T> asList(T... a) {
return new ArrayList<>(a);
}
作用是傳回由指定數組支援的固定大小清單。
注意:這個方法傳回的 ArrayList 不是我們常用的集合類 java.util.ArrayList。這裡的 ArrayList 是 Arrays 的一個内部類 java.util.Arrays.ArrayList。這個内部類有如下屬性和方法:

1 private static class ArrayList<E> extends AbstractList<E>
2 implements RandomAccess, java.io.Serializable
3 {
4 private static final long serialVersionUID = -2764017481108945198L;
5 private final E[] a;
6
7 ArrayList(E[] array) {
8 if (array==null)
9 throw new NullPointerException();
10 a = array;
11 }
12
13 public int size() {
14 return a.length;
15 }
16
17 public Object[] toArray() {
18 return a.clone();
19 }
20
21 public <T> T[] toArray(T[] a) {
22 int size = size();
23 if (a.length < size)
24 return Arrays.copyOf(this.a, size,
25 (Class<? extends T[]>) a.getClass());
26 System.arraycopy(this.a, 0, a, 0, size);
27 if (a.length > size)
28 a[size] = null;
29 return a;
30 }
31
32 public E get(int index) {
33 return a[index];
34 }
35
36 public E set(int index, E element) {
37 E oldValue = a[index];
38 a[index] = element;
39 return oldValue;
40 }
41
42 public int indexOf(Object o) {
43 if (o==null) {
44 for (int i=0; i<a.length; i++)
45 if (a[i]==null)
46 return i;
47 } else {
48 for (int i=0; i<a.length; i++)
49 if (o.equals(a[i]))
50 return i;
51 }
52 return -1;
53 }
54
55 public boolean contains(Object o) {
56 return indexOf(o) != -1;
57 }
58 }
View Code
①、傳回的 ArrayList 數組是一個定長清單,我們隻能對其進行檢視或者修改,但是不能進行添加或者删除操作
通過源碼我們發現該類是沒有add()或者remove() 這樣的方法的,如果對其進行增加或者删除操作,都會調用其父類 AbstractList 對應的方法,而追溯父類的方法最終會抛出 UnsupportedOperationException 異常。如下:
1 String[] str = {"a","b","c"};
2 List<String> listStr = Arrays.asList(str);
3 listStr.set(1, "e");//可以進行修改
4 System.out.println(listStr.toString());//[a, e, c]
5 listStr.add("a");//添加元素會報錯 java.lang.UnsupportedOperationException
②、引用類型的數組和基本類型的數組差別
1 String[] str = {"a","b","c"};
2 List listStr = Arrays.asList(str);
3 System.out.println(listStr.size());//3
4
5 int[] i = {1,2,3};
6 List listI = Arrays.asList(i);
7 System.out.println(listI.size());//1
上面的結果第一個listStr.size()==3,而第二個 listI.size()==1。這是為什麼呢?
我們看源碼,在 Arrays.asList 中,方法聲明為 <T> List<T> asList(T... a)。該方法接收一個可變參數,并且這個可變參數類型是作為泛型的參數。我們知道基本資料類型是不能作為泛型的參數的,但是數組是引用類型,是以數組是可以泛型化的,于是 int[] 作為了整個參數類型,而不是 int 作為參數類型。
是以将上面的方法泛型化補全應該是:
1 String[] str = {"a","b","c"};
2 List<String> listStr = Arrays.asList(str);
3 System.out.println(listStr.size());//3
4
5 int[] i = {1,2,3};
6 List<int[]> listI = Arrays.asList(i);//注意這裡List參數為 int[] ,而不是 int
7 System.out.println(listI.size());//1
8
9 Integer[] in = {1,2,3};
10 List<Integer> listIn = Arrays.asList(in);//這裡參數為int的包裝類Integer,是以集合長度為3
11 System.out.println(listIn.size());//3
③、傳回的清單ArrayList裡面的元素都是引用,不是獨立出來的對象
1 String[] str = {"a","b","c"};
2 List<String> listStr = Arrays.asList(str);
3 //執行更新操作前
4 System.out.println(Arrays.toString(str));//[a, b, c]
5 listStr.set(0, "d");//将第一個元素a改為d
6 //執行更新操作後
7 System.out.println(Arrays.toString(str));//[d, b, c]
這裡的Arrays.toString()方法就是列印數組的内容,後面會介紹。我們看修改集合的内容,原數組的内容也變化了,是以這裡傳入的是引用類型。
④、已知數組資料,如何快速擷取一個可進行增删改查的清單List?
1 String[] str = {"a","b","c"};
2 List<String> listStr = new ArrayList<>(Arrays.asList(str));
3 listStr.add("d");
4 System.out.println(listStr.size());//4
這裡的ArrayList 集合類後面我們會詳細講解,大家目前隻需要知道有這種用法即可。
⑤、Arrays.asList() 方法使用場景
Arrays工具類提供了一個方法asList, 使用該方法可以将一個變長參數或者數組轉換成List 。但是,生成的List的長度是固定的;能夠進行修改操作(比如,修改某個位置的元素);不能執行影響長度的操作(如add、remove等操作),否則會抛出UnsupportedOperationException異常。
是以 Arrays.asList 比較适合那些已經有數組資料或者一些元素,而需要快速建構一個List,隻用于讀取操作,而不進行添加或删除操作的場景。
2、sort
該方法是用于數組排序,在 Arrays 類中有該方法的一系列重載方法,能對7種基本資料類型,包括 byte,char,double,float,int,long,short 等都能進行排序,還有 Object 類型(實作了Comparable接口),以及比較器 Comparator 。
①、基本類型的數組
這裡我們以 int[ ] 為例看看:
1 int[] num = {1,3,8,5,2,4,6,7};
2 Arrays.sort(num);
3 System.out.println(Arrays.toString(num));//[1, 2, 3, 4, 5, 6, 7, 8]
通過調用 sort(int[] a) 方法,将原數組按照升序的順序排列。下面我們通過源碼看看是如何實作排序的:
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
在 Arrays.sort 方法内部調用 DualPivotQuicksort.sort 方法,這個方法的源碼很長,分别對于數組的長度進行了各種算法的劃分,包括快速排序,插入排序,冒泡排序都有使用。詳細源碼可以參考這篇部落格。
②、對象類型數組
該類型的數組進行排序可以實作 Comparable 接口,重寫 compareTo 方法進行排序。
1 String[] str = {"a","f","c","d"};
2 Arrays.sort(str);
3 System.out.println(Arrays.toString(str));//[a, c, d, f]
String 類型實作了 Comparable 接口,内部的 compareTo 方法是按照字典碼進行比較的。
③、沒有實作Comparable接口的,可以通過Comparator實作排序
1 Person[] p = new Person[]{new Person("zhangsan",22),new Person("wangwu",11),new Person("lisi",33)};
2 Arrays.sort(p,new Comparator<Person>() {
3 @Override
4 public int compare(Person o1, Person o2) {
5 if(o1 == null || o2 == null){
6 return 0;
7 }
8 return o1.getPage()-o2.getPage();
9 }
10 });
11 System.out.println(Arrays.toString(p));
3、binarySearch
用二分法查找數組中的某個元素。該方法和 sort 方法一樣,适用于各種基本資料類型以及對象。
注意:二分法是對以及有序的數組進行查找(比如先用Arrays.sort()進行排序,然後調用此方法進行查找)。找到元素傳回下标,沒有則傳回 -1
執行個體:
1 int[] num = {1,3,8,5,2,4,6,7};
2 Arrays.sort(num);
3 System.out.println(Arrays.toString(num));//[1, 2, 3, 4, 5, 6, 7, 8]
4 System.out.println(Arrays.binarySearch(num, 2));//傳回元素的下标 1
具體源碼實作:
1 public static int binarySearch(int[] a, int key) {
2 return binarySearch0(a, 0, a.length, key);
3 }
4 private static int binarySearch0(int[] a, int fromIndex, int toIndex,int key) {
5 int low = fromIndex;
6 int high = toIndex - 1;
7
8 while (low <= high) {
9 int mid = (low + high) >>> 1;//取中間值下标
10 int midVal = a[mid];//取中間值
11
12 if (midVal < key)
13 low = mid + 1;
14 else if (midVal > key)
15 high = mid - 1;
16 else
17 return mid;
18 }
19 return -(low + 1);
20 }
4、copyOf
拷貝數組元素。底層采用 System.arraycopy() 實作,這是一個native方法。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
src:源數組
srcPos:源數組要複制的起始位置
dest:目的數組
destPos:目的數組放置的起始位置
length:複制的長度
注意:src 和 dest都必須是同類型或者可以進行轉換類型的數組。
int[] num1 = {1,2,3};
int[] num2 = new int[3];
System.arraycopy(num1, 0, num2, 0, num1.length);
System.out.println(Arrays.toString(num2));//[1, 2, 3]
/**
* @param original 源數組
* @param newLength //傳回新數組的長度
* @return
*/
public static int[] copyOf(int[] original, int newLength) {
int[] copy = new int[newLength];
System.arraycopy(original, 0, copy, 0,
Math.min(original.length, newLength));
return copy;
}
5、equals 和 deepEquals
①、equals
equals 用來比較兩個數組中對應位置的每個元素是否相等。
八種基本資料類型以及對象都能進行比較。
我們先看看 int類型的數組比較源碼實作:
1 public static boolean equals(int[] a, int[] a2) {
2 if (a==a2)//數組引用相等,則裡面的元素一定相等
3 return true;
4 if (a==null || a2==null)//兩個數組其中一個為null,都傳回false
5 return false;
6
7 int length = a.length;
8 if (a2.length != length)//兩個數組長度不等,傳回false
9 return false;
10
11 for (int i=0; i<length; i++)//通過for循環依次比較數組中每個元素是否相等
12 if (a[i] != a2[i])
13 return false;
14
15 return true;
16 }
在看對象數組的比較:
1 public static boolean equals(Object[] a, Object[] a2) {
2 if (a==a2)
3 return true;
4 if (a==null || a2==null)
5 return false;
6
7 int length = a.length;
8 if (a2.length != length)
9 return false;
10
11 for (int i=0; i<length; i++) {
12 Object o1 = a[i];
13 Object o2 = a2[i];
14 if (!(o1==null ? o2==null : o1.equals(o2)))
15 return false;
16 }
17
18 return true;
19 }
基本上也是通過 equals 來判斷。
②、deepEquals
也是用來比較兩個數組的元素是否相等,不過 deepEquals 能夠進行比較多元數組,而且是任意層次的嵌套數組。
String[][] name1 = {{ "G","a","o" },{ "H","u","a","n"},{ "j","i","e"}};
String[][] name2 = {{ "G","a","o" },{ "H","u","a","n"},{ "j","i","e"}};
System.out.println(Arrays.equals(name1,name2));// false
System.out.println(Arrays.deepEquals(name1,name2));// true
6、fill
該系列方法用于給數組指派,并能指定某個範圍指派。
//給a數組所有元素指派 val
public static void fill(int[] a, int val) {
for (int i = 0, len = a.length; i < len; i++)
a[i] = val;
}
//給從 fromIndex 開始的下标,toIndex-1結尾的下标都指派 val,左閉右開
public static void fill(int[] a, int fromIndex, int toIndex, int val) {
rangeCheck(a.length, fromIndex, toIndex);//判斷範圍是否合理
for (int i = fromIndex; i < toIndex; i++)
a[i] = val;
}
7、toString 和 deepToString
1 public static String toString(int[] a) {
2 if (a == null)
3 return "null";
4 int iMax = a.length - 1;
5 if (iMax == -1)
6 return "[]";
7
8 StringBuilder b = new StringBuilder();
9 b.append('[');
10 for (int i = 0; ; i++) {
11 b.append(a[i]);
12 if (i == iMax)
13 return b.append(']').toString();
14 b.append(", ");
15 }
16 }