强迫自己学习和写作Java stream流介绍

Java stream流介绍

Java 8 引入了 Stream API,这是一个非常强大的工具,专门用于简化对集合(如 ListSetMap)及数组等数据源的操作。它为我们提供了一种更加声明式和函数式的编程风格,允许我们使用流畅的链式操作对数据进行处理。Stream API 主要被用于对集合中的元素进行批量处理,包括过滤、转换、排序和聚合等操作。

什么是 Stream?

Stream 是元素的序列,类似于集合,但与集合不同的是,Stream 不存储数据,它更多的是描述数据的传输和转换。它类似于 SQL 中的查询操作,提供了一组对数据进行操作的函数式工具方法,能够更简洁、更高效地处理集合中的数据。

Stream 是单向的、不可变的,并且只会被操作一次,操作后流就会被消费掉。它的核心特点包括:

  • 惰性求值:Stream 中的中间操作(如 filtermap 等)不会立即执行,只有终端操作(如 collectforEach)执行时,Stream 才会进行真正的计算。
  • 声明式编程:不需要关心数据的具体操作细节,只需要关注要做的操作。
  • 并行处理:可以通过 parallelStream() 轻松实现并行数据处理。

Stream 的两类操作

Stream 中的操作主要分为两大类:中间操作终端操作

1. 中间操作

中间操作会返回一个新的流,但它们不会触发流的执行,只有当终端操作执行时,整个流的处理才会开始。常见的中间操作有:

  • filter(Predicate):根据条件过滤数据,返回符合条件的元素。
  • map(Function):将元素转换为其他类型,通常用于对象属性的提取或数据格式的转换。
  • flatMap(Function):将每个元素映射为一个流,并将这些流扁平化为一个单一流。
  • sorted():对元素进行排序,支持自然排序和自定义排序。
  • distinct():去除流中重复的元素。
  • limit(n)skip(n):截取前 n 个元素,或跳过前 n 个元素。

示例代码:

java


复制代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Alice");
List<String> distinctNames = names.stream()
    .filter(name -> name.length() > 3) // 过滤长度大于3的名字
    .distinct()                        // 去重
    .sorted()                          // 排序
    .collect(Collectors.toList());     // 收集结果到List

2. 终端操作

终端操作会触发流的执行,并且只执行一次。流被消费之后就无法再次使用。常见的终端操作有:

  • forEach(Consumer):对流中的每个元素执行给定的动作。
  • collect(Collector):将流中的元素收集到某种容器中,如 ListSetMap 等。
  • reduce(BinaryOperator):将流中的元素组合成一个值,通常用于聚合操作,如求和、求积。
  • count():返回流中元素的个数。
  • anyMatch(Predicate)allMatch(Predicate)noneMatch(Predicate):判断流中是否有任意、所有、或没有元素满足给定条件。

示例代码:

java


复制代码
int sum = IntStream.range(1, 10) // 创建从1到9的整数流
    .reduce(0, (a, b) -> a + b); // 使用reduce求和

下面将给与一个稍微复杂一点的例子进行讲解

    private List<String> updateAndInsertPerson(List<CjlhData> cjlhData,List<SysUser> accountInfos){
        List<String> returnList = new ArrayList<>();
        List<TalentInfoVo> talentInfoVoList = cjlhData.stream().map(CjlhData::getTalentInfo).filter(ObjectUtil::isNotNull).collect(Collectors.toList());

        List<SysUser> personList = accountInfos.stream()
                .filter(c -> "person".equals(c.getUserType()))
                .collect(Collectors.toList());

        Map<Long,String> personUserNameToIdMap = personList.stream().collect(Collectors.toMap( SysUser::getUserId,SysUser::getUserName));
        //分出各部分是修改的还是插入的(个人)
        if (CollectionUtils.isNotEmpty(personList)){
            List<String> personUserNames =  personList.stream().map(SysUser::getUserName).collect(Collectors.toList());
            List<SysUser> personUsers = sysUserService.selectListByUserNames(personUserNames);
            if (CollectionUtils.isNotEmpty(personUsers)){
                Set<String> personUsersSet = personUsers.stream()
                        .map(SysUser::getUserName)
                        .collect(Collectors.toSet());
                //分出personUserNames在personUsers中的和不在personUsers中的
                Map<Boolean, List<String>> partitionedUserNames = personUserNames.stream()
                        .collect(Collectors.partitioningBy(personUsersSet::contains));

                List<String> inPersonUsers = partitionedUserNames.get(true);
                List<String> notInPersonUsers = partitionedUserNames.get(false);
                if (CollectionUtils.isNotEmpty(inPersonUsers)){
                    //修改的数据 将id附上 如果inPersonUsers在personUsers中的id赋值给inPersonUsers
                    Map<String, Long> userNameToIdMap = personUsers.stream()
                            .collect(Collectors.toMap(SysUser::getUserName, SysUser::getUserId));

                    List<SysUser> updatedPersonList = personList.stream()
                            .filter(u -> inPersonUsers.contains(u.getUserName()))
                            .peek(u -> u.setUserId(userNameToIdMap.get(u.getUserName())))
                            .collect(Collectors.toList());
                    log.info("分离sys_user个人信息更新信息结束");

                    sysUserService.insertOrUpdateUserBatch(updatedPersonList);
                    //查询出所有更新后的userId是否有talentId
                    List<Long> updatedUserIds = updatedPersonList.stream().map(SysUser::getUserId).collect(Collectors.toList());
                    List<TalentInfoVo> talentInfos = talentInfoService.queryByUserIds(updatedUserIds);
                    Map<Long, Long> talentUserIdAndTalentIdMap =  talentInfos.stream().collect(Collectors.toMap(TalentInfoVo::getUserId, TalentInfoVo::getTalentId));

                    log.info("批量更新sys_user个人信息结束");
                    //对talentInfoVoList中与personList的userId相同则将updatedPersonList中返回的userId赋予给匹配的talentInfoVoList的userId,updatedPersonList与personList匹配条件是userName
                    Map<String , Long> updatedUserNameToIdMap = updatedPersonList.stream()
                            .collect(Collectors.toMap(SysUser::getUserName, SysUser::getUserId));

                    talentInfoVoList.stream().forEach(t -> {
                        if (personUserNameToIdMap.get(t.getUserId()) != null){
                            if (updatedUserNameToIdMap.get(personUserNameToIdMap.get(t.getUserId())) != null){
                                t.setUserId(updatedUserNameToIdMap.get(personUserNameToIdMap.get(t.getUserId())));
                                if (ObjectUtil.isNotEmpty(talentUserIdAndTalentIdMap)){
                                    t.setTalentId(talentUserIdAndTalentIdMap.get(t.getUserId()));
                                }
                            }
                        }
                    });

                }
                if (CollectionUtils.isNotEmpty(notInPersonUsers)){
                    //插入的数据
                    List<SysUser> notInPersonUsersList = personList.stream()
                            .filter(u -> notInPersonUsers.contains(u.getUserName()))
                            .peek(u -> u.setUserId(null))
                            .collect(Collectors.toList());
                    log.info("分离sys_user个人信息新增信息结束");
                    sysUserService.insertOrUpdateUserBatch(notInPersonUsersList);
                    log.info("插入sys_user个人信息结束");
                    Map<String, Long> insertUserNameToIdMap = notInPersonUsersList.stream().collect(Collectors.toMap(SysUser::getUserName, SysUser::getUserId));
                    talentInfoVoList.stream().forEach(t -> {
                        if (personUserNameToIdMap.get(t.getUserId()) != null){
                            if (insertUserNameToIdMap.get(personUserNameToIdMap.get(t.getUserId())) != null){
                                t.setUserId(insertUserNameToIdMap.get(personUserNameToIdMap.get(t.getUserId())));
                            }
                        }
                    });
                }
                if (CollectionUtils.isNotEmpty(talentInfoVoList)){
                    log.info("批量插入和更新个人信息开始");
                    List<TalentInfo> talentInfos = BeanUtil.copyToList(talentInfoVoList, TalentInfo.class);
                    talentInfoService.insertOrUpdateBatch(talentInfos);
                    List<String> tPerosn = talentInfos.stream().map(TalentInfo::getUserId).map(String::valueOf).collect(Collectors.toList());
                    returnList.addAll(tPerosn);

                    log.info("批量插入和更新个人信息结束");
                }

            } else {
                //否则就是全部插入的
                if (CollectionUtils.isNotEmpty(personList)){
                    List<SysUser> insert = personList.stream().peek(u -> u.setUserId(null)).collect(Collectors.toList());
                    sysUserService.insertOrUpdateUserBatch(insert);
                    Map<String, Long> insertUserNameToIdMap =  insert.stream().collect(Collectors.toMap(SysUser::getUserName, SysUser::getUserId));
                    talentInfoVoList.stream().forEach(t -> {
                        if (personUserNameToIdMap.get(t.getUserId()) != null){
                            if (insertUserNameToIdMap.get(personUserNameToIdMap.get(t.getUserId())) != null){
                                t.setUserId(insertUserNameToIdMap.get(personUserNameToIdMap.get(t.getUserId())));
                            }
                        }
                    });
                    if (CollectionUtils.isNotEmpty(talentInfoVoList)){
                        List<TalentInfo> talentInfos = BeanUtil.copyToList(talentInfoVoList, TalentInfo.class);
                        talentInfoService.insertOrUpdateBatch(talentInfos);
                        List<String> tPerson = talentInfos.stream().map(TalentInfo::getUserId).map(String::valueOf).collect(Collectors.toList());
                        returnList.addAll(tPerson);
                    }

                }

            }

        }
        return returnList;
    }

在这段代码中,Stream API 被大量运用于数据的过滤、转换和处理。它帮助将传统的迭代过程简化为声明式编程,提高代码的可读性与简洁性。以下是针对代码中使用 stream 流的详细解析:

1. 过滤与映射操作
java


复制代码
List<TalentInfoVo> talentInfoVoList = cjlhData.stream()
    .map(CjlhData::getTalentInfo)
    .filter(ObjectUtil::isNotNull)
    .collect(Collectors.toList());

  • stream(): 将 cjlhData 列表转化为流。
  • map(CjlhData::getTalentInfo): 通过 map 方法,将每个 CjlhData 对象中的 TalentInfo 提取出来。map 是一种转换操作,它将一个元素转化为另一个元素。
  • filter(ObjectUtil::isNotNull): 过滤掉 TalentInfonull 的元素,保留非空数据。
  • collect(Collectors.toList()): 将流中的元素收集回到一个 List 列表中。collect 是终端操作,将流转化为我们需要的集合类型。

这个流程非常直观,替代了传统的嵌套循环与手动判断,使用流可以轻松实现批量数据提取与过滤。

2. 根据条件过滤用户类型
java


复制代码
List<SysUser> personList = accountInfos.stream()
    .filter(c -> "person".equals(c.getUserType()))
    .collect(Collectors.toList());

  • filter(c -> “person”.equals(c.getUserType())): 使用 filter 过滤出用户类型为 "person" 的用户。这是一种典型的条件过滤操作。

这段代码利用 streamfilter 方法,比传统的 for 循环+if 判断更加简洁、清晰。

3. 将 List 转换为 Map
java


复制代码
Map<Long, String> personUserNameToIdMap = personList.stream()
    .collect(Collectors.toMap(SysUser::getUserId, SysUser::getUserName));

  • collect(Collectors.toMap(SysUser::getUserId, SysUser::getUserName)): 这里使用 toMap 方法将 personList 转化为 Map,键是 SysUseruserId,值是 userName。这种转换通常用于快速查找某些对象的键值对信息。
4. 分组和分区
java


复制代码
Map<Boolean, List<String>> partitionedUserNames = personUserNames.stream()
    .collect(Collectors.partitioningBy(personUsersSet::contains));

  • partitioningBy: 这是一个特殊的 Collector,它根据某个条件将元素分为两部分。这里根据 personUsersSet 是否包含某个 userName,将 personUserNames 分为存在和不存在两组。这个操作在传统编程中需要大量的 if 判断,而 Stream API 让它更加直观。
5. 批量更新与插入
java


复制代码
List<SysUser> updatedPersonList = personList.stream()
    .filter(u -> inPersonUsers.contains(u.getUserName()))
    .peek(u -> u.setUserId(userNameToIdMap.get(u.getUserName())))
    .collect(Collectors.toList());

  • peek(u -> u.setUserId(userNameToIdMap.get(u.getUserName())): peek 是一种中间操作,允许我们在流中处理每个元素而不影响主流程。在这里为符合条件的用户设置 userId
  • filter: 用于筛选符合更新条件的用户。
  • collect(Collectors.toList()): 最终将流中的元素收集成列表。

Stream 与并行处理

Java Stream API 还支持 并行流,通过 parallelStream() 可以轻松启用多线程处理数据。当集合数据量较大时,启用并行流可以提升性能,它会将任务拆分给多个线程去处理,然后合并结果。

示例:

java


复制代码
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
int sum = numbers.parallelStream() // 使用并行流
    .reduce(0, Integer::sum);      // 求和

注意:并行流并不总是能提高性能,尤其在数据量较小时,启用多线程可能反而带来不必要的开销。适合并行处理的任务通常是计算密集型或处理大量数据的任务。

Stream 的优势

  1. 简洁性:通过链式调用操作,大大减少了样板代码的编写,提高了代码的可读性。
  2. 函数式编程:Stream API 结合了 Lambda 表达式,带来了更加函数式的编程风格,使代码更加简洁和优雅。
  3. 惰性求值与短路操作:Stream 的惰性求值机制避免了不必要的计算,中间操作不会立即执行,直到需要结果时才开始处理。
  4. 并行处理:简单地将 stream() 替换为 parallelStream(),就可以让程序自动利用多核 CPU 提高处理性能。

典型的 Stream 使用场景

  • 大数据处理:流式处理数据,提高性能。
  • 集合操作:如过滤、转换、排序、合并、去重等。
  • 批量操作:对一组数据执行批量更新、转换等操作。
  • 多线程计算:在需要处理大量数据时,可以使用并行流提高计算效率。

小结

Java Stream API 提供了一种简洁、声明式的方式来操作集合数据。它不仅提升了代码的可读性,还提高了数据处理的效率。通过支持并行流,Stream API 能更好地适应现代多核 CPU 的处理需求,使 Java 开发者能够更加高效地编写程序。

Stream API 的引入,使得 Java 语言更好地支持了函数式编程范式,在现代 Java 开发中已经成为处理数据的主流方式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值