相信我,使用 Stream 真的可以讓代碼更優(yōu)雅!
點(diǎn)擊關(guān)注公眾號(hào):互聯(lián)網(wǎng)架構(gòu)師,后臺(tái)回復(fù) 2T獲取2TB學(xué)習(xí)資源!
原文:blog.csdn.net/qq_41698074/article/details/108502976
前言
雖然 stream在 Java8 中就已經(jīng)被引入,但是大多數(shù)人卻沒(méi)有去使用這個(gè)十分有用的特性,本文就通過(guò)介紹幾個(gè)通過(guò)使用stream讓代碼更簡(jiǎn)潔、可讀,來(lái)讓你了解stream的方便之處。
技巧
數(shù)組轉(zhuǎn)集合
相信經(jīng)常刷LeetCode的小伙伴,偶爾會(huì)遇到需要將List與基本類(lèi)型數(shù)組進(jìn)行互轉(zhuǎn)的情況,然后就需要寫(xiě)像下面這樣的代碼:
// 將 List 元素存儲(chǔ)到數(shù)組中
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
int[] arr = new int[list.size()];
Integer[] temp = list.toArray(new Integer[0]);
for (int i = 0; i < temp.length; i++) {
arr[i] = temp[i];
}
// 將數(shù)組元素 存儲(chǔ)到 List 中
int[] arr = {1, 2, 3, 4, 5};
List<Integer> list = new ArrayList<>();
for (int val : arr) {
list.add(val);
}
以上兩個(gè)轉(zhuǎn)換雖然寫(xiě)著還不算麻煩,但是每次都需要寫(xiě)一個(gè)循環(huán),尤其在數(shù)組轉(zhuǎn)List的時(shí)候還需要使用一個(gè)臨時(shí)數(shù)組,都會(huì)讓人看著很不舒服,但是如果使用了stream就會(huì)大不一樣,用stream實(shí)現(xiàn)了相同功能的代碼如下:
// 將 List 元素存儲(chǔ)到數(shù)組中
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
// 將數(shù)組元素 存儲(chǔ)到 List 中
int[] arr = {1, 2, 3, 4, 5};
List<Integer> list = IntStream.of(arr).boxed().collect(Collectors.toList());
可以發(fā)現(xiàn)通過(guò)使用stream,我們能夠在寫(xiě)代碼的時(shí)候更加連貫,代碼也更加可靠易維護(hù),注意力也可以放在業(yè)務(wù)功能上,相信各位就算對(duì)lambda語(yǔ)法并不是太熟悉,在閱讀上面代碼的時(shí)候,也很容易能夠看懂。
統(tǒng)計(jì)數(shù)組元素中的個(gè)數(shù)
假設(shè)我們現(xiàn)在需要統(tǒng)計(jì)并輸出一個(gè)有重復(fù)元素的數(shù)組中每個(gè)元素及對(duì)應(yīng)元素出現(xiàn)的個(gè)數(shù),相信各位都能夠想到,我們使用一個(gè)Map就很容易解決這個(gè)問(wèn)題,代碼如下:
String[] arr = {"a", "c", "a", "b", "d", "c"};
Map<String, Integer> map = new HashMap<>();
for (String s : arr) {
if (map.containsKey(s)) {
map.put(s, map.get(s) + 1);
} else {
map.put(s, 1);
}
}
map.forEach((key, value) -> System.out.println(key + " : " + value));
如果對(duì)Map中的API更加熟悉的小伙伴,可能會(huì)寫(xiě)出下面這個(gè)更加簡(jiǎn)潔的代碼:
String[] arr = {"a", "c", "a", "b", "d", "c"};
Map<String, Integer> map = new HashMap<>();
for (String s : arr) {
map.put(s, map.getOrDefault(s, 0) + 1);
}
map.forEach((key, value) -> System.out.println(key + " : " + value));
但是,如果使用stream,我們還能寫(xiě)出更加簡(jiǎn)潔的代碼,同樣不需要寫(xiě)煩人的循環(huán)了,而且只需兩行代碼即可(為了提高可讀性,進(jìn)行了換行):
String[] arr = {"a", "c", "a", "b", "d", "c"};
Stream.of(arr)
.collect(Collectors.toMap(k -> k, k -> 1, Integer::sum))
.forEach((k, v) -> System.out.println(k + " : " + v));
注意
在上面的代碼中,Collectors.toMap(k -> k, k -> 1, Integer::sum)這一部分可能不好理解,對(duì)于這里面的三個(gè)參數(shù),第一個(gè)參數(shù)代表將arr中的每一個(gè)元素作為Map中的key,第二個(gè)參數(shù)代表每一個(gè)key所對(duì)應(yīng)的value,在這里每一個(gè)元素都對(duì)應(yīng)個(gè)數(shù)1,第三個(gè)參數(shù)代表,如果存在相同的key,該如何進(jìn)行合并,這里通過(guò)使用Integer::sum,代表將具有相同key的元素進(jìn)行合并時(shí),其value進(jìn)行相加,這樣便實(shí)現(xiàn)了每個(gè)元素個(gè)數(shù)的統(tǒng)計(jì)。
基本數(shù)據(jù)類(lèi)型的數(shù)組自定義排序
有時(shí)我們會(huì)遇到對(duì)基本數(shù)據(jù)類(lèi)型的數(shù)組進(jìn)行自定義排序的情況,不同于包裝類(lèi)型的數(shù)組和集合可以直接使用比較器,我們只能通過(guò)將基本數(shù)組類(lèi)型的數(shù)組轉(zhuǎn)為包裝類(lèi)型或者存儲(chǔ)在集合中,在排序完成后再轉(zhuǎn)為基本類(lèi)型的數(shù)組,再者,我們只能通過(guò)手寫(xiě)排序算法,修改排序算法中的比較進(jìn)行實(shí)現(xiàn)。
不管是哪種方法,我們都沒(méi)辦法將精力放在邏輯功能上,必須寫(xiě)一些額外的代碼,甚至是修改底層邏輯,就像下面的代碼一樣(實(shí)現(xiàn)數(shù)組逆序):
int[] arr = {1, 5, 9, 7, 2, 3, 7, -1, 0, 3};
// 將數(shù)組轉(zhuǎn)為包裝類(lèi)型再進(jìn)行自定義排序
Integer[] temp = new Integer[arr.length];
for (int i = 0; i < arr.length; i++) {
temp[i] = arr[i];
}
Arrays.sort(temp, Comparator.reverseOrder());
for (int i = 0; i < temp.length; i++) {
arr[i] = temp[i];
}
// 將數(shù)組轉(zhuǎn)為集合類(lèi)型再進(jìn)行自定義排序
List<Integer> list = new ArrayList<>();
for (int val : arr) {
list.add(val);
}
list.sort(Collections.reverseOrder());
for (int i = 0; i < list.size(); i++) {
arr[i] = list.get(i);
}
// 通過(guò)手寫(xiě)排序算法修改比較規(guī)則實(shí)現(xiàn)
// 為了讓代碼更加簡(jiǎn)潔,使用了最暴力且沒(méi)有優(yōu)化的冒泡排序
int[] arr = {1, 5, 9, 7, 2, 3, 7, -1, 0, 3};
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] < arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
可以發(fā)現(xiàn)以上幾種方法,我們都需要寫(xiě)很多代碼,無(wú)法將注意力集中在設(shè)計(jì)自定義排序這個(gè)問(wèn)題上,但是通過(guò)使用stream,我們就可以寫(xiě)出下面這樣簡(jiǎn)潔的代碼(如果愿意的話,你也可以把一系列的鏈?zhǔn)讲僮鲗?xiě)在一行上,但為了代碼的可讀性,不建議那么做):
int[] arr = {1, 5, 9, 7, 2, 3, 7, -1, 0, 3};
arr = IntStream.of(arr)
.boxed()
.sorted(Comparator.reverseOrder())
.mapToInt(Integer::intValue)
.toArray();
注意
在這里其實(shí)為了實(shí)現(xiàn)數(shù)組的逆序,我們只需要調(diào)用Arrays的sort方法,然后再進(jìn)行數(shù)組元素的反轉(zhuǎn)即可,不過(guò)因?yàn)槭菫榱酥v解自定義排序,大多數(shù)情況下不會(huì)是數(shù)組逆序這么簡(jiǎn)單,所以我就寫(xiě)了更加通用一些的代碼。
統(tǒng)計(jì)數(shù)組中前 k 個(gè)個(gè)高頻元素
在最后,我們通過(guò)一道題來(lái)進(jìn)行實(shí)戰(zhàn)以便更好的體驗(yàn)stream的強(qiáng)大之處,當(dāng)然我們?cè)诰毩?xí)該題的時(shí)候,更需要從算法的角度去考慮該題的解法,不過(guò)在本文,我們主要為了講解stream的使用,所以就不去考慮算法的東西了,而如果使用stream,我們就可以寫(xiě)出下面這樣簡(jiǎn)單易懂的代碼:
class Solution {
public int[] topKFrequent(int[] nums, int k) {
return Arrays.stream(nums)
.boxed()
.collect(Collectors.toMap(e -> e, e -> 1, Integer::sum))
.entrySet()
.stream()
.sorted((m1, m2) -> m2.getValue() - m1.getValue())
.limit(k)
.mapToInt(Map.Entry::getKey)
.toArray();
}
}
總結(jié)
本文介紹了幾個(gè)簡(jiǎn)單、實(shí)用的stream使用技巧,當(dāng)然stream的應(yīng)用遠(yuǎn)不止此,希望通過(guò)本文,能夠激發(fā)起你學(xué)習(xí)stream的興趣,本文若有錯(cuò)誤之處,也歡迎你的指正。
正文結(jié)束
1.心態(tài)崩了!稅前2萬(wàn)4,到手1萬(wàn)4,年終獎(jiǎng)扣稅方式1月1日起施行~
2.深圳一普通中學(xué)老師工資單曝光,秒殺程序員,網(wǎng)友:敢問(wèn)是哪個(gè)學(xué)校畢業(yè)的?
3.從零開(kāi)始搭建創(chuàng)業(yè)公司后臺(tái)技術(shù)棧
5.清華大學(xué):2021 元宇宙研究報(bào)告!
6.為什么國(guó)內(nèi) 996 干不過(guò)國(guó)外的 955呢?

