Java8-stream介紹

【Java8】 Stream常見使用
Stream 基本介紹
1、Java 8中提供了一個新的附加包,名為Java.util.stream。這個包由類、接口和枚舉組成,允許對元素進(jìn)行函數(shù)式操作,您可以使用stream來過濾、收集、打印和從一個數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換到另一個;
2、Stream API 借助于同樣新出現(xiàn)的 Lambda 表達(dá)式,極大的提高編程效率和程序可讀性。
Stream使用好處
1、函數(shù)式編程,提高代碼開發(fā)效率和簡潔性;
2、提供多種對集合數(shù)據(jù)的過濾、聚合、轉(zhuǎn)換操作,比較方便;
3、將一些sql中復(fù)雜數(shù)據(jù)統(tǒng)計放到服務(wù)處理,提升性能
Stream使用缺點
1、團(tuán)隊開發(fā)習(xí)慣如果不常用,可能會影響代碼易讀性;
2、使用stream編寫代碼,不容易排錯和調(diào)試。
常見使用示例
1、基本數(shù)據(jù)對象定義
?//定義一個UserInfo類,包含userName、age、sex字段 ?@Data ?public class UserInfo { ??private String userName; ??private Integer age; ??private String sex; ?} ? /** ? * 構(gòu)造測試數(shù)據(jù) ? * @return ?*/ ?public static List<UserInfo> builderData(){ ????List<UserInfo> userInfoList = new ArrayList<UserInfo>(); ????userInfoList.add(UserInfo.builder().age(18).idCard(123456).name("小紅").memo("備注").build()); ????userInfoList.add(UserInfo.builder().age(16).idCard(13456).name("小軍").memo("備注1").build()); ????userInfoList.add(UserInfo.builder().age(20).idCard(123456).name("小花").memo("備注2").build()); ????userInfoList.add(UserInfo.builder().age(18).idCard(123456).name("小容").memo("備注3").build()); ????userInfoList.add(UserInfo.builder().age(18).idCard(123456).name("小容").memo("備注6").build()); ????userInfoList.add(UserInfo.builder().age(26).idCard(12346).name("小海").memo("備注4").build()); ????userInfoList.add(UserInfo.builder().age(26).idCard(12346).name("小海").memo("備注5").build()); ????return userInfoList; ??}
2、Stream的創(chuàng)建方法:
① 通過 java.util.Collection.stream() 方法用集合創(chuàng)建流
?List<String> list = Arrays.asList("learn","Java8","stream"); ?// 創(chuàng)建順序流 ?Stream<String> stream = list.stream(); ?// 創(chuàng)建并行流 ?Stream<String> parallelStream = list.parallelStream();
② 使用java.util.Arrays.stream(T[] array)方法用數(shù)組創(chuàng)建流
?String[] arrays = {"a", "b", "c", "d", "e"}; ?Stream<String> arrayStream = Arrays.stream(arrays);
③ Stream的靜態(tài)方法:of()、iterate()、generate()
?Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6); ?Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(3); ?stream2.forEach(System.out::println); ?Stream<Double> stream3 = Stream.generate(Math::random).limit(3); ?stream3.forEach(System.out::println)
3、Stream數(shù)據(jù)過濾操作
filter:篩選,是按照一定的規(guī)則校驗流中的元素,將符合條件的元素提取到新的流中的操作
?List<UserInfo> userInfoList = builderData(); ????userInfoList.stream().filter(o -> o.getAge() > 20).forEach(System.out::println);
4、映射操作(map、flatMap、peek)
① map 一個元素類型為 T 的流轉(zhuǎn)換成元素類型為 R 的流,這個方法傳入一個Function的函數(shù)式接口,接收一個泛型T,返回泛型R,map函數(shù)的定義,返回的流,表示的泛型是R對象;
?userInfoList.stream().map(UserInfo::getAge).forEach(System.out::println);
② flatMap 接收一個函數(shù)作為參數(shù),將流中的每個值都換成另一個流,然后把所有流連接成一個流。
總之:與Map功能類似,區(qū)別在于將結(jié)合A的流轉(zhuǎn)換成B流;
?List<String> list1 = Arrays.asList("h,e,l,l", "1,2,3,4"); ????List<String> list2 = list1.stream().flatMap(o -> { ??????String[] split = o.split(","); ??????return Arrays.stream(split); ????}).collect(Collectors.toList()); ????System.out.println("處理前:" + list1); ????System.out.println("處理后:" + list2);
③ peek 操作 一般用于不想改變流中元素本身的類型或者只想元素的內(nèi)部狀態(tài)時;
而 map 則用于改變流中元素本身類型,即從元素中派生出另一種類型的操作。
?Stream<String> stream = Stream.of("hello", "felord.cn"); ?stream.peek(System.out::println).collect(Collectors.toList());
④ 另外還有mapToInt、mapToLong、mapToDouble、flatMapToDouble、flatMapToInt、flatMapToLong
以上這些操作是map和flatMap的特例版,也就是針對特定的數(shù)據(jù)類型進(jìn)行映射處理
5、去重、排序、獲取指定個數(shù)操作
① distinct:返回由該流的不同元素組成的流,
1、根據(jù) Object.equals(Object)),distinct 使用hashCode()和equals()方法獲取不同元素。
2、我們的類必須實現(xiàn)hashCode()和equals()方法
② sorted:返回由該流的元素組成的流,并根據(jù)自然順序排序,該接口有兩種形式:無參和有參數(shù),如:
?Stream<T> sorted(); ?Stream<T> sorted(Comparator<? super T> comparator);
區(qū)別:傳入比較器的參數(shù),可以自定義這個比較器,即自定義比較規(guī)則。
③ limit:獲取流中n個元素返回的流
?List<UserInfo> userInfoList = builderData(); ????userInfoList.stream().limit(3).forEach(System.out::println);
6、anyMatch:、allMatch、noneMatch、findFirst、findAny
① anyMatch:Stream 中只要有一個元素符合傳入的 predicate,返回 true;
boolean anyMatch(Predicate<? super T> predicate);
② allMatch:Stream 中全部元素符合傳入的 predicate,返回 true;
boolean allMatch(Predicate<? super T> predicate);
③ noneMatch:Stream 中沒有一個元素符合傳入的 predicate,返回 true.
boolean noneMatch(Predicate<? super T> predicate);
④ findFirst:用于返回滿足條件的第一個元素(但是該元素是封裝在Optional類中)
Optional<T> findFirst();
⑤ findAny:返回流中的任意元素(但是該元素也是封裝在Optional類中)
Optional<T> findAny();
findAny()操作,返回的元素是不確定的,使用findAny()是為了更高效的性能
?// 以上操作使用示例 ?List<UserInfo> userInfoList = builderData(); ????boolean anyMatch = userInfoList.stream().anyMatch(o -> o.getAge() >18); ????boolean allMatch = userInfoList.stream().allMatch(o -> o.getAge() >18); ????boolean noneMatch = userInfoList.stream().noneMatch(o -> o.getAge() >18); ????System.out.println(anyMatch+","+allMatch+","+noneMatch); ????userInfoList.stream().filter(o -> o.getAge() > 35).findFirst().ifPresent(System.out::println); ????System.out.println(userInfoList.stream().filter(o -> o.getAge() > 20).findAny().get());
7、foreach、forEachOrdered
注意里面的變量必須為final申明,因為lambda中,使用的外部變量必須是最終的,不可變的
① forEach:該方法接收一個Lambda表達(dá)式,然后在Stream的每一個元素上執(zhí)行該表達(dá)式
void forEach(Consumer<? super T> action);
② forEachOrdered:該方法接收一個Lambda表達(dá)式,然后按順序在Stream的每一個元素上執(zhí)行該表達(dá)式,并發(fā)環(huán)境下仍然不能保證順序
void forEachOrdered(Consumer<? super T> action);
③ reduce:方法接收一個函數(shù)作為累加器,數(shù)組中的每個值(從左到右)開始縮減,最終計算為一個值
8、終止操作collect:稱為收集器,是一個終端操作,它接收的參數(shù)是將流中的元素累積到匯總結(jié)果的各種方式
方法含義說明toList將流中的元素收集到一個List中toSet將流中的元素收集到一個Set中toCollection將流中的元素收集到一個Collection中toMap將流中的元素映射收集到一個Map中counting統(tǒng)計流中的元素個數(shù)summingInt計算流中指定int字段的累加總和。針對不同類型的數(shù)字類型,有不同的方法,比如summingDouble等averagingInt計算流中指定int字段的平均值。針對不同類型的數(shù)字類型,有不同的方法,比如averagingLong等joining將流中所有元素(或者元素的指定字段)字符串值進(jìn)行拼接,可以指定拼接連接符,或者首尾拼接字符maxBy根據(jù)給定的比較器,選擇出值最大的元素minBy根據(jù)給定的比較器,選擇出值最小的元素groupingBy根據(jù)給定的分組函數(shù)的值進(jìn)行分組,輸出一個Map對象partitioningBy根據(jù)給定的分區(qū)函數(shù)的值進(jìn)行分區(qū),輸出一個Map對象,且key始終為布爾值類型collectingAndThen包裹另一個收集器,對其結(jié)果進(jìn)行二次加工轉(zhuǎn)換reducing從給定的初始值開始,將元素進(jìn)行逐個的處理,最終將所有元素計算為最終的1個值輸出
下面對幾個容易混淆的進(jìn)行舉例使用
① partitioningBy 使用,將數(shù)據(jù)按照條件分成兩組
?Map<Boolean, List<UserInfo>> booleanListMap = userInfoList.stream().collect(Collectors.partitioningBy(item -> item.getAge() > 27));
② collectingAndThen 使用,包裹收集器
?Integer maxAge = userInfoList.stream().collect(Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(UserInfo::getAge)), Optional::get)).getAge();
③ reducing使用
?userInfoList.stream().map(o -> new BigDecimal(String.valueOf(o.getAge()))).collect(Collectors.reducing(BigDecimal.ZERO,BigDecimal::add));