【Java8】 Stream常见使用

75 阅读6分钟

Stream 基本介绍

1、Java 8中提供了一个新的附加包,名为Java.util.stream。这个包由类、接口和枚举组成,允许对元素进行函数式操作,您可以使用stream来过滤、收集、打印和从一个数据结构转换到另一个; 2、Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。

Stream使用好处

1、函数式编程,提高代码开发效率和简洁性; 2、提供多种对集合数据的过滤、聚合、转换操作,比较方便; 3、将一些sql中复杂数据统计放到服务处理,提升性能

Stream使用缺点

1、团队开发习惯如果不常用,可能会影响代码易读性; 2、使用stream编写代码,不容易排错和调试。


常见使用示例

1、基本数据对象定义

//定义一个UserInfo类,包含userName、age、sex字段
@Data
public class UserInfo {
    private String userName;
    private Integer age;
    private String sex;
}
 /**
  * 构造测试数据
  * @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的创建方法: ① 通过 java.util.Collection.stream() 方法用集合创建流

List<String> list = Arrays.asList("learn","Java8","stream"); 
// 创建顺序流
Stream<String> stream = list.stream(); 
// 创建并行流
Stream<String> parallelStream = list.parallelStream(); 

② 使用java.util.Arrays.stream(T[] array)方法用数组创建流

String[] arrays = {"a", "b", "c", "d", "e"};
Stream<String> arrayStream = Arrays.stream(arrays);

③ Stream的静态方法: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数据过滤操作 filter:筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作

List<UserInfo> userInfoList = builderData();
        userInfoList.stream().filter(o -> o.getAge() > 20).forEach(System.out::println);

4、映射操作(map、flatMap、peek)

① map 一个元素类型为 T 的流转换成元素类型为 R 的流,这个方法传入一个Function的函数式接口,接收一个泛型T,返回泛型R,map函数的定义,返回的流,表示的泛型是R对象;

userInfoList.stream().map(UserInfo::getAge).forEach(System.out::println);

② flatMap 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。 总之:与Map功能类似,区别在于将结合A的流转换成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 操作 一般用于不想改变流中元素本身的类型或者只想元素的内部状态时;

而 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的特例版,也就是针对特定的数据类型进行映射处理 5、去重、排序、获取指定个数操作 ① distinct:返回由该流的不同元素组成的流, 1、根据 Object.equals(Object)),distinct 使用hashCode()和equals()方法获取不同元素。
2、我们的类必须实现hashCode()和equals()方法

② sorted:返回由该流的元素组成的流,并根据自然顺序排序,该接口有两种形式:无参和有参数,如:

Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);

区别:传入比较器的参数,可以自定义这个比较器,即自定义比较规则。 ③ 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 findFirst(); ⑤ findAny:返回流中的任意元素(但是该元素也是封装在Optional类中) Optional 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表达式,然后在Stream的每一个元素上执行该表达式 void forEach(Consumer<? super T> action); ② forEachOrdered:该方法接收一个Lambda表达式,然后按顺序在Stream的每一个元素上执行该表达式,并发环境下仍然不能保证顺序 void forEachOrdered(Consumer<? super T> action); ③ reduce:方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终计算为一个值 8、终止操作collect:称为收集器,是一个终端操作,它接收的参数是将流中的元素累积到汇总结果的各种方式

方法含义说明
toList将流中的元素收集到一个List中
toSet将流中的元素收集到一个Set中
toCollection将流中的元素收集到一个Collection中
toMap将流中的元素映射收集到一个Map中
counting统计流中的元素个数
summingInt计算流中指定int字段的累加总和。针对不同类型的数字类型,有不同的方法,比如summingDouble等
averagingInt计算流中指定int字段的平均值。针对不同类型的数字类型,有不同的方法,比如averagingLong等
joining将流中所有元素(或者元素的指定字段)字符串值进行拼接,可以指定拼接连接符,或者首尾拼接字符
maxBy根据给定的比较器,选择出值最大的元素
minBy根据给定的比较器,选择出值最小的元素
groupingBy根据给定的分组函数的值进行分组,输出一个Map对象
partitioningBy根据给定的分区函数的值进行分区,输出一个Map对象,且key始终为布尔值类型
collectingAndThen包裹另一个收集器,对其结果进行二次加工转换
reducing从给定的初始值开始,将元素进行逐个的处理,最终将所有元素计算为最终的1个值输出

下面对几个容易混淆的进行举例使用

① partitioningBy 使用,将数据按照条件分成两组

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));

制作不易,如果大家看到这里,请给个关注和三连,谢谢大家,后续会继续分享更新其他技术干货。欢迎留言区交流。 更多详细技术视频,欢迎进入B站,点击如下视频地址学习,

www.bilibili.com/video/BV1pz… 如果对你有帮助,记得点个三连和关注,感谢。