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

