相信我,使用 Stream 真的可以讓代碼更優(yōu)雅!
答應(yīng)我, 不要再用 if (obj != null) 判空了 20個(gè)示例!詳解 Java8 Stream 用法,從此告別shi山(垃圾代碼) 利用Java8新特征,重構(gòu)傳統(tǒng)設(shè)計(jì)模式,你學(xué)會(huì)了嗎? 竟然有一半的人不知道 for 與 foreach 的區(qū)別??? 利用多線程批量拆分 List 導(dǎo)入數(shù)據(jù)庫,效率杠杠的!
前言
雖然 stream在 Java8 中就已經(jīng)被引入,但是大多數(shù)人卻沒有去使用這個(gè)十分有用的特性,本文就通過介紹幾個(gè)通過使用stream讓代碼更簡(jiǎn)潔、可讀,來讓你了解stream的方便之處。
技巧
數(shù)組轉(zhuǎn)集合
相信經(jīng)常刷LeetCode的小伙伴,偶爾會(huì)遇到需要將List與基本類型數(shù)組進(jìn)行互轉(zhuǎn)的情況,然后就需要寫像下面這樣的代碼:
//?將?List?元素存儲(chǔ)到數(shù)組中
List?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??arr[i]?=?temp[i];
}
//?將數(shù)組元素?存儲(chǔ)到?List?中
int[]?arr?=?{1,?2,?3,?4,?5};
List?list?=?new?ArrayList<>();
for?(int?val?:?arr)?{
?list.add(val);
}
以上兩個(gè)轉(zhuǎn)換雖然寫著還不算麻煩,但是每次都需要寫一個(gè)循環(huán),尤其在數(shù)組轉(zhuǎn)List的時(shí)候還需要使用一個(gè)臨時(shí)數(shù)組,都會(huì)讓人看著很不舒服,但是如果使用了stream就會(huì)大不一樣,用stream實(shí)現(xiàn)了相同功能的代碼如下:
//?將?List?元素存儲(chǔ)到數(shù)組中
List?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?list?=?IntStream.of(arr).boxed().collect(Collectors.toList());
可以發(fā)現(xiàn)通過使用stream,我們能夠在寫代碼的時(shí)候更加連貫,代碼也更加可靠易維護(hù),注意力也可以放在業(yè)務(wù)功能上,相信各位就算對(duì)lambda語法并不是太熟悉,在閱讀上面代碼的時(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è)問題,代碼如下:
String[]?arr?=?{"a",?"c",?"a",?"b",?"d",?"c"};
Map?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ì)寫出下面這個(gè)更加簡(jiǎn)潔的代碼:
String[]?arr?=?{"a",?"c",?"a",?"b",?"d",?"c"};
Map?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,我們還能寫出更加簡(jiǎn)潔的代碼,同樣不需要寫煩人的循環(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)行合并,這里通過使用Integer::sum,代表將具有相同key的元素進(jìn)行合并時(shí),其value進(jìn)行相加,這樣便實(shí)現(xiàn)了每個(gè)元素個(gè)數(shù)的統(tǒng)計(jì)。
基本數(shù)據(jù)類型的數(shù)組自定義排序
有時(shí)我們會(huì)遇到對(duì)基本數(shù)據(jù)類型的數(shù)組進(jìn)行自定義排序的情況,不同于包裝類型的數(shù)組和集合可以直接使用比較器,我們只能通過將基本數(shù)組類型的數(shù)組轉(zhuǎn)為包裝類型或者存儲(chǔ)在集合中,在排序完成后再轉(zhuǎn)為基本類型的數(shù)組,再者,我們只能通過手寫排序算法,修改排序算法中的比較進(jìn)行實(shí)現(xiàn)。
不管是哪種方法,我們都沒辦法將精力放在邏輯功能上,必須寫一些額外的代碼,甚至是修改底層邏輯,就像下面的代碼一樣(實(shí)現(xiàn)數(shù)組逆序):
int[]?arr?=?{1,?5,?9,?7,?2,?3,?7,?-1,?0,?3};
//?將數(shù)組轉(zhuǎn)為包裝類型再進(jìn)行自定義排序
Integer[]?temp?=?new?Integer[arr.length];
for?(int?i?=?0;?i?????temp[i]?=?arr[i];
}
Arrays.sort(temp,?Comparator.reverseOrder());
for?(int?i?=?0;?i?????arr[i]?=?temp[i];
}
//?將數(shù)組轉(zhuǎn)為集合類型再進(jìn)行自定義排序
List?list?=?new?ArrayList<>();
for?(int?val?:?arr)?{
????list.add(val);
}
list.sort(Collections.reverseOrder());
for?(int?i?=?0;?i?????arr[i]?=?list.get(i);
}
//?通過手寫排序算法修改比較規(guī)則實(shí)現(xiàn)
//?為了讓代碼更加簡(jiǎn)潔,使用了最暴力且沒有優(yōu)化的冒泡排序
int[]?arr?=?{1,?5,?9,?7,?2,?3,?7,?-1,?0,?3};
for?(int?i?=?0;?i?????for?(int?j?=?0;?j?1;?j++)?{
????????if?(arr[j]?1])?{
????????????int?temp?=?arr[j];
????????????arr[j]?=?arr[j?+?1];
????????????arr[j?+?1]?=?temp;
????????}
????}
}
可以發(fā)現(xiàn)以上幾種方法,我們都需要寫很多代碼,無法將注意力集中在設(shè)計(jì)自定義排序這個(gè)問題上,但是通過使用stream,我們就可以寫出下面這樣簡(jiǎn)潔的代碼(如果愿意的話,你也可以把一系列的鏈?zhǔn)讲僮鲗懺谝恍猩希珵榱舜a的可讀性,不建議那么做):
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)即可,不過因?yàn)槭菫榱酥v解自定義排序,大多數(shù)情況下不會(huì)是數(shù)組逆序這么簡(jiǎn)單,所以我就寫了更加通用一些的代碼。
統(tǒng)計(jì)數(shù)組中前 k 個(gè)個(gè)高頻元素
在最后,我們通過一道題來進(jìn)行實(shí)戰(zhàn)以便更好的體驗(yàn)stream的強(qiáng)大之處,當(dāng)然我們?cè)诰毩?xí)該題的時(shí)候,更需要從算法的角度去考慮該題的解法,不過在本文,我們主要為了講解stream的使用,所以就不去考慮算法的東西了,而如果使用stream,我們就可以寫出下面這樣簡(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)不止此,希望通過本文,能夠激發(fā)起你學(xué)習(xí)stream的興趣,本文若有錯(cuò)誤之處,也歡迎你的指正。
來源:blog.csdn.net/qq_41698074/article/
details/108502976
最后,再給大家推薦一個(gè)GitHub項(xiàng)目,該項(xiàng)目整理了上千本常用技術(shù)PDF,技術(shù)書籍都可以在這里找到。 GitHub地址:https://github.com/hello-go-maker/cs-books 電子書已經(jīng)更新好了,拿走不謝,記得點(diǎn)一個(gè)star,持續(xù)更新中...

