Java8新特性 StreamAPI實例詳解
Stream結(jié)果收集
面試官:說說你常用的StreamAPI。
結(jié)果收集到集合中
public static void main(String[] args){ // Stream<String> stream = Stream.of("aa", "bb", "cc"); List<String> list = Stream.of("aa", "bb", "cc","aa") .collect(Collectors.toList()); System.out.println(list); // 收集到 Set集合中 Set<String> set = Stream.of("aa", "bb", "cc", "aa") .collect(Collectors.toSet()); System.out.println(set); // 如果需要獲取的類型為具體的實現(xiàn),比如:ArrayList HashSet ArrayList<String> arrayList = Stream.of("aa", "bb", "cc", "aa") //.collect(Collectors.toCollection(() -> new ArrayList<>())); .collect(Collectors.toCollection(ArrayList::new)); System.out.println(arrayList); HashSet<String> hashSet = Stream.of("aa", "bb", "cc", "aa") .collect(Collectors.toCollection(HashSet::new)); System.out.println(hashSet); }
輸出:
[aa, bb, cc, aa]
[aa, bb, cc]
[aa, bb, cc, aa]
[aa, bb, cc]
結(jié)果收集到數(shù)組中
Stream中提供了toArray方法來將結(jié)果放到一個數(shù)組中,返回值類型是Object[],如果我們要指定返回的類型,那么可以使用另一個重載的toArray(IntFunction f)方法。
public static void main(String[] args){ Object[] objects = Stream.of("aa", "bb", "cc", "aa") .toArray(); // 返回的數(shù)組中的元素是 Object類型 System.out.println(Arrays.toString(objects)); // 如果我們需要指定返回的數(shù)組中的元素類型 String[] strings = Stream.of("aa", "bb", "cc", "aa") .toArray(String[]::new); System.out.println(Arrays.toString(strings)); }
對流中的數(shù)據(jù)做聚合計算
當(dāng)我們使用Stream流處理數(shù)據(jù)后,可以像數(shù)據(jù)庫的聚合函數(shù)一樣對某個字段進(jìn)行操作,比如獲得最大值,最小值,求和,平均值,統(tǒng)計數(shù)量。
public static void main(String[] args) { // 獲取年齡的最大值 Optional<Person> maxAge = Stream.of( new Person("張三", 18) , new Person("李四", 22) , new Person("張三", 13) , new Person("王五", 15) , new Person("張三", 19) ).collect(Collectors.maxBy((p1, p2) -> p1.getAge() - p2.getAge())); System.out.println("最大年齡:" + maxAge.get()); // 獲取年齡的最小值 Optional<Person> minAge = Stream.of( new Person("張三", 18) , new Person("李四", 22) , new Person("張三", 13) , new Person("王五", 15) , new Person("張三", 19) ).collect(Collectors.minBy((p1, p2) -> p1.getAge() - p2.getAge())); System.out.println("最新年齡:" + minAge.get()); // 求所有人的年齡之和 Integer sumAge = Stream.of( new Person("張三", 18) , new Person("李四", 22) , new Person("張三", 13) , new Person("王五", 15) , new Person("張三", 19) ) //.collect(Collectors.summingInt(s -> s.getAge())) .collect(Collectors.summingInt(Person::getAge)) ; System.out.println("年齡總和:" + sumAge); // 年齡的平均值 Double avgAge = Stream.of( new Person("張三", 18) , new Person("李四", 22) , new Person("張三", 13) , new Person("王五", 15) , new Person("張三", 19) ).collect(Collectors.averagingInt(Person::getAge)); System.out.println("年齡的平均值:" + avgAge); // 統(tǒng)計數(shù)量 Long count = Stream.of( new Person("張三", 18) , new Person("李四", 22) , new Person("張三", 13) , new Person("王五", 15) , new Person("張三", 19) ).filter(p->p.getAge() > 18) .collect(Collectors.counting()); System.out.println("滿足條件的記錄數(shù):" + count); }
對流中數(shù)據(jù)做分組操作
當(dāng)我們使用Stream流處理數(shù)據(jù)后,可以根據(jù)某個屬性將數(shù)據(jù)分組。
public static void main(String[] args){ // 根據(jù)賬號對數(shù)據(jù)進(jìn)行分組 Map<String, List<Person>> map1 = Stream.of( new Person("張三", 18, 175) , new Person("李四", 22, 177) , new Person("張三", 14, 165) , new Person("李四", 15, 166) , new Person("張三", 19, 182) ).collect(Collectors.groupingBy(Person::getName)); map1.forEach((k,v)-> System.out.println("k=" + k +"\t"+ "v=" + v)); System.out.println("-----------"); // 根據(jù)年齡分組 如果大于等于18 成年否則未成年 Map<String, List<Person>> map2 = Stream.of( new Person("張三", 18, 175) , new Person("李四", 22, 177) , new Person("張三", 14, 165) , new Person("李四", 15, 166) , new Person("張三", 19, 182) ).collect(Collectors.groupingBy(p -> p.getAge() >= 18 ? "成年" : "未成年")); map2.forEach((k,v)-> System.out.println("k=" + k +"\t"+ "v=" + v)); }
輸出結(jié)果:
k=李四 v=[Person{name='李四', age=22, height=177}, Person{name='李四', age=15, height=166}]
k=張三 v=[Person{name='張三', age=18, height=175}, Person{name='張三', age=14, height=165}, Person{name='張三', age=19, height=182}]
-----------
k=未成年 v=[Person{name='張三', age=14, height=165}, Person{name='李四', age=15, height=166}]
k=成年 v=[Person{name='張三', age=18, height=175}, Person{name='李四', age=22, height=177}, Person{name='張三', age=19, height=182}]
多級分組: 先根據(jù)name分組然后根據(jù)年齡分組。
public static void main(String[] args){ // 先根據(jù)name分組,然后根據(jù)age(成年和未成年)分組 Map<String,Map<Object,List<Person>>> map = Stream.of( new Person("張三", 18, 175) , new Person("李四", 22, 177) , new Person("張三", 14, 165) , new Person("李四", 15, 166) , new Person("張三", 19, 182) ).collect(Collectors.groupingBy( Person::getName ,Collectors.groupingBy(p->p.getAge()>=18?"成年":"未成年" ) )); map.forEach((k,v)->{ System.out.println(k); v.forEach((k1,v1)->{ System.out.println("\t"+k1 + "=" + v1); }); }); }
輸出結(jié)果:
李四
未成年=[Person{name='李四', age=15, height=166}]
成年=[Person{name='李四', age=22, height=177}]
張三
未成年=[Person{name='張三', age=14, height=165}]
成年=[Person{name='張三', age=18, height=175}, Person{name='張三', age=19, height=182}]
對流中的數(shù)據(jù)做分區(qū)操作
Collectors.partitioningBy會根據(jù)值是否為true,把集合中的數(shù)據(jù)分割為兩個列表,一個true列表,一個false列表。
public static void main(String[] args){ Map<Boolean, List<Person>> map = Stream.of( new Person("張三", 18, 175) , new Person("李四", 22, 177) , new Person("張三", 14, 165) , new Person("李四", 15, 166) , new Person("張三", 19, 182) ).collect(Collectors.partitioningBy(p -> p.getAge() > 18)); map.forEach((k,v)-> System.out.println(k+"\t" + v)); }
輸出結(jié)果:
false [Person{name='張三', age=18, height=175}, Person{name='張三', age=14, height=165}, Person{name='李四', age=15, height=166}]
true [Person{name='李四', age=22, height=177}, Person{name='張三', age=19, height=182}]
對流中的數(shù)據(jù)做拼接
Collectors.joining會根據(jù)指定的連接符,將所有的元素連接成一個字符串。
public static void main(String[] args){ String s1 = Stream.of( new Person("張三", 18, 175) , new Person("李四", 22, 177) , new Person("張三", 14, 165) , new Person("李四", 15, 166) , new Person("張三", 19, 182) ).map(Person::getName) .collect(Collectors.joining()); // 張三李四張三李四張三 System.out.println(s1); String s2 = Stream.of( new Person("張三", 18, 175) , new Person("李四", 22, 177) , new Person("張三", 14, 165) , new Person("李四", 15, 166) , new Person("張三", 19, 182) ).map(Person::getName) .collect(Collectors.joining("_")); // 張三_李四_張三_李四_張三 System.out.println(s2); String s3 = Stream.of( new Person("張三", 18, 175) , new Person("李四", 22, 177) , new Person("張三", 14, 165) , new Person("李四", 15, 166) , new Person("張三", 19, 182) ).map(Person::getName) .collect(Collectors.joining("_", "###", "$$$")); // ###張三_李四_張三_李四_張三$$$ System.out.println(s3); }
并行的Stream流
串行的Stream流
我們前面使用的Stream流都是串行,也就是在一個線程上面執(zhí)行。
并行流
parallelStream其實就是一個并行執(zhí)行的流,它通過默認(rèn)的ForkJoinPool,可以提高多線程任務(wù)的速度。
獲取并行流
我們可以通過兩種方式來獲取并行流。
- 通過List接口中的parallelStream方法來獲取
- 通過已有的串行流轉(zhuǎn)換為并行流(parallel)
public static void main(String[] args){ List<Integer> list = new ArrayList<>(); // 通過List 接口 直接獲取并行流 Stream<Integer> integerStream = list.parallelStream(); // 將已有的串行流轉(zhuǎn)換為并行流 Stream<Integer> parallel = Stream.of(1, 2, 3).parallel(); }
并行流操作
public static void main(String[] args){ Stream.of(1,4,2,6,1,5,9) .parallel() // 將流轉(zhuǎn)換為并發(fā)流,Stream處理的時候就會通過多線程處理 .filter(s->{ System.out.println(Thread.currentThread() + " s=" +s); return s > 2; }).count(); }
并行流和串行流對比
我們通過for循環(huán),串行Stream流,并行Stream流來對500000000億個數(shù)字求和。來看消耗時間。
public class Test { private static long times = 500000000; private long start; @Before public void befor(){ start = System.currentTimeMillis(); } @After public void end(){ long end = System.currentTimeMillis(); System.out.println("消耗時間:" + (end - start)); } /** * 普通for循環(huán) 消耗時間:138 */ @Test public void test01(){ System.out.println("普通for循環(huán):"); long res = 0; for (int i = 0; i < times; i++) { res += i; } } /** * 串行流處理 * 消耗時間:203 */ @Test public void test02(){ System.out.println("串行流:serialStream"); LongStream.rangeClosed(0,times) .reduce(0,Long::sum); } /** * 并行流處理 消耗時間:84 */ @Test public void test03(){ LongStream.rangeClosed(0,times) .parallel() .reduce(0,Long::sum); } }
通過案例我們可以看到parallelStream的效率是最高的。
Stream并行處理的過程會分而治之,也就是將一個大的任務(wù)切分成了多個小任務(wù),這表示每個任務(wù)都是一個線程操作。
線程安全問題
在多線程的處理下,肯定會出現(xiàn)數(shù)據(jù)安全問題。如下:
@Test public void test(){ List<Integer> list = new ArrayList<>(); for (int i = 0; i < 1000; i++) { list.add(i); } System.out.println(list.size()); List<Integer> listNew = new ArrayList<>(); // 使用并行流來向集合中添加數(shù)據(jù) list.parallelStream() //.forEach(s->listNew.add(s)); .forEach(listNew::add); System.out.println(listNew.size());// 839 }
針對這個問題,我們的解決方案有哪些呢?
- 加同步鎖
- 使用線程安全的容器
- 通過Stream中的toArray/collect操作
以上就是Java8新特性 StreamAPI實例詳解的詳細(xì)內(nèi)容,更多關(guān)于Java8新特性 StreamAPI的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Spring實戰(zhàn)之緩存使用condition操作示例
這篇文章主要介紹了Spring實戰(zhàn)之緩存使用condition操作,結(jié)合實例形式分析了Spring緩存使用condition具體配置、屬性、領(lǐng)域模型等相關(guān)操作技巧與注意事項,需要的朋友可以參考下2020-01-01基于SpringBoot和Leaflet的行政區(qū)劃地圖掩膜效果實戰(zhàn)教程
本文講解的是一種圖層級的掩膜,即使用行政區(qū)劃圖層來進(jìn)行掩膜,使用場景為,用戶只需要在地圖頁面中展示目標(biāo)行政區(qū)劃內(nèi)的影像信息,對于行政邊界外的影像,這篇文章主要介紹了基于SpringBoot和Leaflet的行政區(qū)劃地圖掩膜效果實戰(zhàn),需要的朋友可以參考下2024-05-05Java Online Exam在線考試系統(tǒng)的實現(xiàn)
讀萬卷書不如行萬里路,只學(xué)書上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+springboot+vue+jsp+mysql+maven實現(xiàn)Online Exam在線考試系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-11-11Spring?Boot中的max-http-header-size配置方式
這篇文章主要介紹了Spring?Boot中的max-http-header-size配置方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09Mybatis注解方式完成輸入?yún)?shù)為list的SQL語句拼接方式
這篇文章主要介紹了Mybatis注解方式完成輸入?yún)?shù)為list的SQL語句拼接方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11