java.util.Arrays 類就是為陣列而生的專用工具類,基本上常見的對陣列的操作,Arrays 類都考慮到了,這讓我由衷地覺得,是時候給該類的作者 Josh Bloch、Neal Gafter、John Rose 點個贊了,
(我是怎么知道作者名的?看原始碼就可以,小伙伴們,裝逼吧)
Arrays 都可以干嘛呢?常見的有:
- 創建陣列
- 比較陣列
- 陣列排序
- 陣列檢索
- 陣列轉流
- 列印陣列
- 陣列轉 List
- setAll(沒想好中文名)
- parallelPrefix(沒想好中文名)
那接下來,小伙伴們是不是已經迫不及待想要和二哥一起來打怪進階了,走你,
01、創建陣列
使用 Arrays 類創建陣列可以通過以下三個方法:
- copyOf,復制指定的陣列,截取或用 null 填充
- copyOfRange,復制指定范圍內的陣列到一個新的陣列
- fill,對陣列進行填充
1)copyOf,直接來看例子:
String[] intro = new String[] { "沉", "默", "王", "二" };
String[] revised = Arrays.copyOf(intro, 3);
String[] expanded = Arrays.copyOf(intro, 5);
System.out.println(Arrays.toString(revised));
System.out.println(Arrays.toString(expanded));
revised 和 expanded 是復制后的新陣列,長度分別是 3 和 5,指定的陣列長度是 4,來看一下輸出結果:
[沉, 默, 王]
[沉, 默, 王, 二, null]
看到沒?revised 截取了最后一位,因為長度是 3 嘛;expanded 用 null 填充了一位,因為長度是 5,
2)copyOfRange,直接來看例子:
String[] intro = new String[] { "沉", "默", "王", "二" };
String[] abridgement = Arrays.copyOfRange(intro, 0, 3);
System.out.println(Arrays.toString(abridgement));
copyOfRange() 方法需要三個引數,第一個是指定的陣列,第二個是起始位置(包含),第三個是截止位置(不包含),來看一下輸出結果:
[沉, 默, 王]
0 的位置是“沉”,3 的位置是“二”,也就是說截取了從 0 位(包含)到 3 位(不包含)的陣列元素,那假如說下標超出了陣列的長度,會發生什么呢?
String[] abridgementExpanded = Arrays.copyOfRange(intro, 0, 6);
System.out.println(Arrays.toString(abridgementExpanded));
結束位置此時為 6,超出了指定陣列的長度 4,來看一下輸出結果:
[沉, 默, 王, 二, null, null]
仍然使用了 null 進行填充,為什么要這么做呢?小伙伴們思考一下,我想是作者考慮到了陣列越界的問題,不然每次呼叫 Arrays 類就要先判斷很多次長度,很麻煩,
3)fill,直接來看例子:
String[] stutter = new String[4];
Arrays.fill(stutter, "沉默王二");
System.out.println(Arrays.toString(stutter));
使用 new 關鍵字創建了一個長度為 4 的陣列,然后使用 fill() 方法將 4 個位置填充為“沉默王二”,來看一下輸出結果:
[沉默王二, 沉默王二, 沉默王二, 沉默王二]
如果想要一個元素完全相同的陣列時, fill() 方法就派上用場了,
02、比較陣列
Arrays 類的 equals() 方法用來判斷兩個陣列是否相等,來看下面這個例子:
String[] intro = new String[] { "沉", "默", "王", "二" };
boolean result = Arrays.equals(new String[] { "沉", "默", "王", "二" }, intro);
System.out.println(result);
boolean result1 = Arrays.equals(new String[] { "沉", "默", "王", "三" }, intro);
System.out.println(result1);
輸出結果如下所示:
true
false
指定的陣列為沉默王二四個字,比較的陣列一個是沉默王二,一個是沉默王三,所以 result 為 true,result1 為 false,
簡單看一下 equals() 方法的原始碼:
public static boolean equals(Object[] a, Object[] a2) {
if (a==a2)
return true;
if (a==null || a2==null)
return false;
int length = a.length;
if (a2.length != length)
return false;
for (int i=0; i<length; i++) {
if (!Objects.equals(a[i], a2[i]))
return false;
}
return true;
}
因為陣列是一個物件,所以先使用“==”運算子進行判斷,如果不相等,再判斷是否為 null,兩個都為 null,回傳 false;緊接著判斷 length,不等的話,回傳 false;否則的話,依次呼叫 Objects.equals() 比較相同位置上的元素是否相等,
感覺非常嚴謹,這也就是學習原始碼的意義,鑒賞的同時,學習,
除了 equals() 方法,還有另外一個訣竅可以判斷兩個陣列是否相等,盡管可能會出現誤差(概率非常小),那就是 Arrays.hashCode() 方法,先來看一下該方法的原始碼:
public static int hashCode(Object a[]) {
if (a == null)
return 0;
int result = 1;
for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());
return result;
}
哈希演算法本身是非常嚴謹的,所以如果兩個陣列的哈希值相等,那幾乎可以判斷兩個陣列是相等的,
String[] intro = new String[] { "沉", "默", "王", "二" };
System.out.println(Arrays.hashCode(intro));
System.out.println(Arrays.hashCode(new String[] { "沉", "默", "王", "二" }));
來看一下輸出結果:
868681617
868681617
兩個陣列的哈希值相等,畢竟元素是一樣的,但這樣確實不夠嚴謹,優先使用 Objects.equals() 方法,
03、陣列排序
Arrays 類的 sort() 方法用來判斷兩個陣列是否相等,來看下面這個例子:
String[] intro1 = new String[] { "chen", "mo", "wang", "er" };
String[] sorted = Arrays.copyOf(intro1, 4);
Arrays.sort(sorted);
System.out.println(Arrays.toString(sorted));
由于排序會改變原有的陣列,所以我們使用了 copyOf() 方法重新復制了一份,來看一下輸出結果:
[chen, er, mo, wang]
可以看得出,按照的是首字母的升序進行排列的,基本資料型別是按照雙軸快速排序的,參考資料型別是按照 TimSort 排序的,使用了 Peter McIlroy 的“樂觀排序和資訊理論復雜性”中的技術,
04、陣列檢索
陣列排序后就可以使用 Arrays 類的 binarySearch() 方法進行二分查找了,否則的話,只能線性檢索,效率就會低很多,
String[] intro1 = new String[] { "chen", "mo", "wang", "er" };
String[] sorted = Arrays.copyOf(intro1, 4);
Arrays.sort(sorted);
int exact = Arrays.binarySearch(sorted, "wang");
System.out.println(exact);
int caseInsensitive = Arrays.binarySearch(sorted, "Wang", String::compareToIgnoreCase);
System.out.println(caseInsensitive);
binarySearch() 方法既可以精確檢索,也可以模糊檢索,比如說忽略大小寫,來看一下輸出結果:
3
3
排序后的結果是 [chen, er, mo, wang],所以檢索出來的下標是 3,
05、陣列轉流
Stream 流非常強大,需要入門的小伙伴可以查看我之前寫的一篇文章:
一文帶你入門Java Stream流,太強了
Arrays 類的 stream() 方法可以將陣列轉換成流:
String[] intro = new String[] { "沉", "默", "王", "二" };
System.out.println(Arrays.stream(intro).count());
還可以為 stream() 方法指定起始下標和結束下標:
System.out.println(Arrays.stream(intro, 1, 2).count());
如果下標的范圍有誤的時候,比如說從 2 到 1 結束,則程式會拋出 ArrayIndexOutOfBoundsException 例外:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: origin(2) > fence(1)
at java.base/java.util.Spliterators.checkFromToBounds(Spliterators.java:387)
06、列印陣列
關于陣列的列印方式,我之前單獨寫過一篇文章:
列印Java陣列最優雅的方式是什么?
里面談了很多種陣列列印的方式,因為陣列是一個物件,直接 System.out.println 的話,結果是這樣的:
[Ljava.lang.String;@3d075dc0
那最優雅的方式,其實文章里面已經出現過很多次了,就是 Arrays.toString():
public static String toString(Object[] 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(String.valueOf(a[i]));
if (i == iMax)
return b.append(']').toString();
b.append(", ");
}
}
小伙伴可以好好欣賞一下這段原始碼,感覺考慮得非常周到,
07、陣列轉 List
盡管陣列非常強大,但它自身可以操作的工具方法很少,比如說判斷陣列中是否包含某個值,轉成 List 的話,就簡便多了,
String[] intro = new String[] { "沉", "默", "王", "二" };
List<String> rets = Arrays.asList(intro);
System.out.println(rets.contains("二"));
不過需要注意的是,Arrays.asList() 回傳的是 java.util.Arrays.ArrayList,并不是 java.util.ArrayList,它的長度是固定的,無法進行元素的洗掉或者添加,
rets.add("三");
rets.remove("二");
執行這兩個方法的時候,會拋出例外:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.AbstractList.add(AbstractList.java:153)
at java.base/java.util.AbstractList.add(AbstractList.java:111)
要想操作元素的話,需要多一步轉化:
List<String> rets1 = new ArrayList<>(Arrays.asList(intro));
rets1.add("三");
rets1.remove("二");
08、setAll
Java 8 新增了 setAll() 方法,它提供了一個函式式編程的入口,可以對陣列的元素進行填充:
int[] array = new int[10];
Arrays.setAll(array, i -> i * 10);
System.out.println(Arrays.toString(array));
這段代碼什么意思呢?i 就相當于是陣列的下標,值從 0 開始,到 9 結束,那么 i * 10 就意味著 0 * 10 開始,到 9 * 10 結束,來看一下輸出結果:
[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]
比之前的 fill() 方法強大多了,對吧?不再填充的是相同的元素,
09、parallelPrefix
parallelPrefix() 方法和 setAll() 方法一樣,也是 Java 8 之后提供的,提供了一個函式式編程的入口,通過遍歷陣列中的元素,將當前下標位置上的元素與它之前下標的元素進行操作,然后將操作后的結果覆寫當前下標位置上的元素,
int[] arr = new int[] { 1, 2, 3, 4};
Arrays.parallelPrefix(arr, (left, right) -> left + right);
System.out.println(Arrays.toString(arr));
上面代碼中有一個 Lambda 運算式((left, right) -> left + right),需要入門的小伙伴可以查看我之前寫的一篇文章:
Lambda 運算式入門,看這篇就夠了
那為了讓小伙伴看得更明白一些,我們把上面這段代碼稍微改造一下:
int[] arr = new int[]{1, 2, 3, 4};
Arrays.parallelPrefix(arr, (left, right) -> {
System.out.println(left + "," + right);
return left + right;
});
System.out.println(Arrays.toString(arr));
先來看一下輸出結果:
1,2
3,3
6,4
[1, 3, 6, 10]
也就是說, Lambda 運算式執行了三次:
- 第一次是 1 和 2 相加,結果是 3,替換下標為 1 的位置
- 第二次是 3 和 3 相加,結果是 6,也就是第一次的結果和下標為 2 的元素相加的結果
- 第三次是 6 和 4 相加,結果是 10,也就是第二次的結果和下標為 3 的元素相加的結果
有點強大,對吧?
10、總結
好了,我親愛的小伙伴們,以上就是本文的全部內容了,能看到這里的都是最優秀的程式員,二哥必須要為你點個贊,
我是沉默王二,一枚有趣的程式員,如果覺得文章對你有點幫助,請微信搜索「 沉默王二 」第一時間閱讀,回復【666】更有我為你精心準備的 500G 高清教學視頻(已分門別類),
本文 GitHub 已經收錄,有大廠面試完整考點,歡迎 Star,
原創不易,莫要白票,請你為本文點個贊吧,這將是我寫作更多優質文章的最強動力,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/151705.html
標籤:Java
上一篇:Mybatis報錯:Mapped Statements collection does not contain value for x包.x類.x方法
