日常開發(fā)中,我們會使用各種工具類,利用封裝好的輪子,能讓我們的開發(fā)事半功倍。但是在JDK中,有一個特別的工具類——java.lang.Arrays.class,其源碼實現(xiàn)還是挺精湛,接下來讓我們來揭開它神秘的面紗。
java.util.Arrays 類是 JDK 提供的一個工具類,用來處理數(shù)組的各種方法,而且每個方法基本上都是靜態(tài)方法,能直接通過類名Arrays調(diào)用。
1、asList
public static < T > List< T > asList(T... a) {
return new ArrayList< >(a);
}
作用是返回由指定數(shù)組支持的固定大小列表。
注意 :這個方法返回的 ArrayList 不是我們常用的集合類 java.util.ArrayList。這里的 ArrayList 是 Arrays 的一個內(nèi)部類 java.util.Arrays.ArrayList。這個內(nèi)部類有如下屬性和方法:
private static class ArrayList< E > extends AbstractList< E >
implements RandomAccess, java.io.Serializable{
private static final long serialVersionUID = -2764017481108945198L;
private final E[] a;
ArrayList(E[] array) {
if (array==null){
throw new NullPointerException();
}
a = array;
}
public int size() {
return a.length;
}
public Object[] toArray() {
return a.clone();
}
public < T > T[] toArray(T[] a) {
int size = size();
if (a.length < size){
return Arrays.copyOf(this.a, size,(Class< ? extends T[] >) a.getClass());
}
System.arraycopy(this.a, 0, a, 0, size);
if (a.length > size){
a[size] = null;
}
return a;
}
public E get(int index) {
return a[index];
}
public E set(int index, E element) {
E oldValue = a[index];
a[index] = element;
return oldValue;
}
public int indexOf(Object o) {
if (o==null) {
for (int i=0; i< a.length; i++){
if (a[i]==null){
return i;
}
}
} else {
for (int i=0; i< a.length; i++){
if (o.equals(a[i])){
return i;
}
}
}
return -1;
}
public boolean contains(Object o) {
return indexOf(o) != -1;
}
}
①、返回的 ArrayList 數(shù)組是一個定長列表,我們只能對其進(jìn)行查看或者修改,但是不能進(jìn)行添加或者刪除操作
通過源碼我們發(fā)現(xiàn)該類是沒有add()或者remove() 這樣的方法的,如果對其進(jìn)行增加或者刪除操作,都會調(diào)用其父類 AbstractList 對應(yīng)的方法,而追溯父類的方法最終會拋出 UnsupportedOperationException 異常。如下:
String[] str = {"a","b","c"};
List< String > listStr = Arrays.asList(str);
listStr.set(1, "e");//可以進(jìn)行修改
System.out.println(listStr.toString());//[a, e, c]
listStr.add("a");//添加元素會報錯 java.lang.UnsupportedOperationException
②、引用類型的數(shù)組和基本類型的數(shù)組區(qū)別
String[] str = {"a","b","c"};
List listStr = Arrays.asList(str);
System.out.println(listStr.size());//3
int[] i = {1,2,3};
List listI = Arrays.asList(i);
System.out.println(listI.size());//1
上面的結(jié)果第一個listStr.size()==3,而第二個 listI.size()==1。這是為什么呢?
我們看源碼,在 Arrays.asList 中,方法聲明為ListasList(T... a)。該方法接收一個可變參數(shù),并且這個可變參數(shù)類型是作為泛型的參數(shù)。我們知道基本數(shù)據(jù)類型是不能作為泛型的參數(shù)的,但是數(shù)組是引用類型,所以數(shù)組是可以泛型化的,于是 int[] 作為了整個參數(shù)類型,而不是 int 作為參數(shù)類型。
所以將上面的方法泛型化補全應(yīng)該是:
String[] str = {"a","b","c"};
List< String > listStr = Arrays.asList(str);
System.out.println(listStr.size());//3
int[] i = {1,2,3};
List< int[] > listI = Arrays.asList(i);//注意這里L(fēng)ist參數(shù)為 int[] ,而不是 int
System.out.println(listI.size());//1
Integer[] in = {1,2,3};
List< Integer > listIn = Arrays.asList(in);//這里參數(shù)為int的包裝類Integer,所以集合長度為3
System.out.println(listIn.size());//3
③、返回的列表ArrayList里面的元素都是引用,不是獨立出來的對象
String[] str = {"a","b","c"};
List< String > listStr = Arrays.asList(str);
//執(zhí)行更新操作前
System.out.println(Arrays.toString(str));//[a, b, c]
listStr.set(0, "d");//將第一個元素a改為d
//執(zhí)行更新操作后
System.out.println(Arrays.toString(str));//[d, b, c]
這里的Arrays.toString()方法就是打印數(shù)組的內(nèi)容,后面會介紹。我們看修改集合的內(nèi)容,原數(shù)組的內(nèi)容也變化了,所以這里傳入的是引用類型。
④、已知數(shù)組數(shù)據(jù),如何快速獲取一個可進(jìn)行增刪改查的列表List?
String[] str = {"a","b","c"};
List< String > listStr = new ArrayList< >(Arrays.asList(str));
listStr.add("d");
System.out.println(listStr.size());//4
這里的ArrayList 集合類后面我們會詳細(xì)講解,大家目前只需要知道有這種用法即可。
⑤、Arrays.asList() 方法使用場景
Arrays工具類提供了一個方法asList, 使用該方法可以將一個變長參數(shù)或者數(shù)組轉(zhuǎn)換成List 。但是,生成的List的長度是固定的;能夠進(jìn)行修改操作(比如,修改某個位置的元素);不能執(zhí)行影響長度的操作(如add、remove等操作),否則會拋出UnsupportedOperationException異常。
所以 Arrays.asList 比較適合那些已經(jīng)有數(shù)組數(shù)據(jù)或者一些元素,而需要快速構(gòu)建一個List,只用于讀取操作,而不進(jìn)行添加或刪除操作的場景。
2、sort
該方法是用于數(shù)組排序,在 Arrays 類中有該方法的一系列重載方法,能對7種基本數(shù)據(jù)類型,包括 byte,char,double,float,int,long,short 等都能進(jìn)行排序,還有 Object 類型(實現(xiàn)了Comparable接口),以及比較器 Comparator 。
①、基本類型的數(shù)組
這里我們以 int[ ] 為例看看:
int[] num = {1,3,8,5,2,4,6,7};
Arrays.sort(num);
System.out.println(Arrays.toString(num));//[1, 2, 3, 4, 5, 6, 7, 8]
通過調(diào)用 sort(int[] a) 方法,將原數(shù)組按照升序的順序排列。下面我們通過源碼看看是如何實現(xiàn)排序的:
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
在 Arrays.sort 方法內(nèi)部調(diào)用 DualPivotQuicksort.sort 方法,這個方法的源碼很長,分別對于數(shù)組的長度進(jìn)行了各種算法的劃分,包括快速排序,插入排序,冒泡排序都有使用。詳細(xì)源碼可以參考這篇博客。
②、對象類型數(shù)組
該類型的數(shù)組進(jìn)行排序可以實現(xiàn) Comparable 接口,重寫 compareTo 方法進(jìn)行排序。
String[] str = {"a","f","c","d"};
Arrays.sort(str);
System.out.println(Arrays.toString(str));//[a, c, d, f]
String 類型實現(xiàn)了 Comparable 接口,內(nèi)部的 compareTo 方法是按照字典碼進(jìn)行比較的。
③、沒有實現(xiàn)Comparable接口的,可以通過Comparator實現(xiàn)排序
Person[] p = new Person[]{new Person("zhangsan",22),new Person("wangwu",11),new Person("lisi",33)};
Arrays.sort(p,new Comparator< Person >() {
@Override
public int compare(Person o1, Person o2) {
if(o1 == null || o2 == null){
return 0;
}
return o1.getPage()-o2.getPage();
}
});
System.out.println(Arrays.toString(p));
3、binarySearch
用二分法查找數(shù)組中的某個元素。該方法和 sort 方法一樣,適用于各種基本數(shù)據(jù)類型以及對象。
注意:二分法是對以及有序的數(shù)組進(jìn)行查找(比如先用Arrays.sort()進(jìn)行排序,然后調(diào)用此方法進(jìn)行查找)。找到元素返回下標(biāo),沒有則返回 -1
實例:
int[] num = {1,3,8,5,2,4,6,7};
Arrays.sort(num);
System.out.println(Arrays.toString(num));//[1, 2, 3, 4, 5, 6, 7, 8]
System.out.println(Arrays.binarySearch(num, 2));//返回元素的下標(biāo) 1
具體源碼實現(xiàn):
public static int binarySearch(int[] a, int key) {
return binarySearch0(a, 0, a.length, key);
}
private static int binarySearch0(int[] a, int fromIndex, int toIndex,int key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) > >> 1;//取中間值下標(biāo)
int midVal = a[mid];//取中間值
if (midVal < key){
low = mid + 1;
else if (midVal > key)
high = mid - 1;
}
else{
return mid;
}
}
return -(low + 1);
}
4、copyOf
拷貝數(shù)組元素。底層采用 System.arraycopy() 實現(xiàn),這是一個native方法。
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
src:源數(shù)組
srcPos:源數(shù)組要復(fù)制的起始位置
dest:目的數(shù)組
destPos:目的數(shù)組放置的起始位置
length:復(fù)制的長度
注意:src 和 dest都必須是同類型或者可以進(jìn)行轉(zhuǎn)換類型的數(shù)組。
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 源數(shù)組
* @param newLength //返回新數(shù)組的長度
* @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、fill
該系列方法用于給數(shù)組賦值,并能指定某個范圍賦值。
//給a數(shù)組所有元素賦值 val
public static void fill(int[] a, int val) {
for (int i = 0, len = a.length; i < len; i++){
a[i] = val;
}
}
//給從 fromIndex 開始的下標(biāo),toIndex-1結(jié)尾的下標(biāo)都賦值 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;
}
}
6、toString 和 deepToString
toString 用來打印一維數(shù)組的元素,而 deepToString 用來打印多層次嵌套的數(shù)組元素。
public static String toString(int[] a) {
if (a == null){
return "null";
}
int iMax = a.length - 1;
if (iMax == -1){
return "[]";
}
StringBuilder b = new StringBuilder();
b.append('[');
for (int i = 0; ; i++) {
b.append(a[i]);
if (i == iMax){
return b.append(']').toString();
}
b.append(", ");
}
}
7、小結(jié)
好了,這就是JDK中java.lang.Arrays 類的源碼解析。
-
JAVA
+關(guān)注
關(guān)注
19文章
2966瀏覽量
104707 -
源碼
+關(guān)注
關(guān)注
8文章
639瀏覽量
29186 -
數(shù)組
+關(guān)注
關(guān)注
1文章
417瀏覽量
25940 -
JDK
+關(guān)注
關(guān)注
0文章
81瀏覽量
16594
發(fā)布評論請先 登錄
相關(guān)推薦
評論