使用Java Stream groupingBy时如何合并元素?循环逻辑迁移遇阻
解决Java Stream分组合并IOType集合的问题
需求回顾
以User对象的getStringSeq分割后的前两个元素作为分组key,将相同key的User对象合并其IOType集合(依赖HashSet自动去重)。
原循环逻辑说明
原循环通过HashMap实现核心逻辑:
- 遍历每个User,生成分组key
- 若key不存在:创建新的HashSet存放当前IOType,设置到User后存入Map
- 若key已存在:直接将当前IOType添加到已有User的IOType集合中
错误Stream代码的问题点
你尝试的Stream代码存在以下问题:
- 错误使用
mapping收集器:仅将元素映射后收集为List,未实现分组内元素的合并逻辑,最终得到List<List<User>>,偏离需求的Map<String, User>目标 - 映射逻辑混乱:返回null和
UserNavAuthsLoginDTO,与原需求的User对象合并逻辑完全不符,无法正确收集并合并IOType
正确实现方案
方案1:使用Collectors.toMap(推荐,逻辑更直接)
toMap适合这种key唯一、需要合并值的场景,直接指定key生成、值初始化和合并规则:
Map<String, User> resultMap = userList.stream() .collect(Collectors.toMap( // 生成分组key user -> { String[] arr = user.getStringSeq().split(DELIMITER); return String.join(DELIMITER, arr[MENU_IDX], arr[GROUP_IDX]); }, // 初始化每个key对应的User:创建新User并设置初始IOType集合 user -> { String[] arr = user.getStringSeq().split(DELIMITER); User mergedUser = new User(); // 复制原User的其他必要字段(如getStringSeq等) mergedUser.setStringSeq(user.getStringSeq()); Set<IOType> ioTypeSet = new HashSet<>(); ioTypeSet.add(IOType.valueOf(arr[IO_TYPE_IDX])); mergedUser.setIoTypes(ioTypeSet); return mergedUser; }, // 合并规则:将新User的IOType添加到已有User的集合中 (existingUser, newUser) -> { String[] arr = newUser.getStringSeq().split(DELIMITER); existingUser.getIoTypes().add(IOType.valueOf(arr[IO_TYPE_IDX])); return existingUser; } ));
如果允许复用原集合中的第一个User(和原循环逻辑完全一致),可调整为:
Map<String, User> resultMap = userList.stream() .collect(Collectors.toMap( user -> { String[] arr = user.getStringSeq().split(DELIMITER); return String.join(DELIMITER, arr[MENU_IDX], arr[GROUP_IDX]); }, user -> { String[] arr = user.getStringSeq().split(DELIMITER); if (user.getIoTypes() == null) { Set<IOType> ioTypeSet = new HashSet<>(); ioTypeSet.add(IOType.valueOf(arr[IO_TYPE_IDX])); user.setIoTypes(ioTypeSet); } return user; }, (existing, incoming) -> { String[] arr = incoming.getStringSeq().split(DELIMITER); existing.getIoTypes().add(IOType.valueOf(arr[IO_TYPE_IDX])); return existing; } ));
方案2:使用Collectors.groupingBy配合collectingAndThen
如果偏好使用groupingBy,可以通过collectingAndThen将分组后的List
Map<String, User> resultMap = userList.stream() .collect(Collectors.groupingBy( user -> { String[] arr = user.getStringSeq().split(DELIMITER); return String.join(DELIMITER, arr[MENU_IDX], arr[GROUP_IDX]); }, Collectors.collectingAndThen( Collectors.toList(), users -> { // 以第一个User为基础合并 User mergedUser = users.get(0); Set<IOType> ioTypes = new HashSet<>(); // 遍历分组内所有User,收集所有IOType for (User u : users) { String[] arr = u.getStringSeq().split(DELIMITER); ioTypes.add(IOType.valueOf(arr[IO_TYPE_IDX])); } mergedUser.setIoTypes(ioTypes); return mergedUser; } ) ));
内容的提问来源于stack exchange,提问作者Ji Chang-jin
相关产品推荐
相关产品推荐

