如何用Java Stream实现两个List<Map>的Inner Join并合并Map
Java Stream实现List
我懂你现在的困扰——你写的代码得到了笛卡尔积(9条数据),还只保留了其中一个列表的内容,这完全不是你想要的内连接效果。咱们先拆解下问题出在哪,再给出正确的实现:
你的代码问题分析
- 错误的flatMap使用:你直接把
leftData.stream().flatMap(t1 -> rightData.stream()),这相当于把每个订单和所有用户做了全配对,先产生了orders.size() * users.size()条数据(也就是9条)。 - 无效的过滤条件:filter里的
t是rightData的元素(用户Map),你试图用t.get(joinColumnTableLeft)获取订单的user_id,但这个键在用户Map里根本不存在,要么会得到null导致equals抛异常,要么筛选逻辑完全不成立。 - 没有合并Map:就算筛选对了,你最后收集的还是原来的单个Map,没有把订单和用户的内容合并到一起。
正确的实现代码
下面是用纯Java Stream实现内连接并合并Map的方案,完全不需要第三方库:
import java.util.*; import java.util.stream.Collectors; public class StreamJoinExample { public static void main(String[] args) { // 初始化你的订单和用户数据 List<Map<String, String>> orders = List.of( Map.of("item_id", "1", "item", "item-1", "user_id", "1"), Map.of("item_id", "2", "item", "item-2", "user_id", "2"), Map.of("item_id", "3", "item", "item-3", "user_id", "3") ); List<Map<String, String>> users = List.of( Map.of("user_id", "1", "name", "abh", "email", "abh@bit.com"), Map.of("user_id", "2", "name", "pol", "email", "pol@bit.com"), Map.of("user_id", "3", "name", "tre", "email", "tre@bit.com") ); // 定义连接键 String joinKey = "user_id"; // 执行内连接并合并Map List<Map<String, String>> joinedResult = orders.stream() // 对每个订单,找到匹配的用户 .flatMap(order -> users.stream() // 筛选user_id匹配的用户 .filter(user -> Objects.equals(order.get(joinKey), user.get(joinKey))) // 合并订单和用户的键值对到新Map .map(user -> { // 先复制订单的所有内容 Map<String, String> mergedMap = new HashMap<>(order); // 再添加用户的所有内容(如果有重复键,用户的会覆盖订单的,这里user_id值一致不影响) mergedMap.putAll(user); return mergedMap; }) ) .collect(Collectors.toList()); // 打印结果验证 joinedResult.forEach(System.out::println); } }
代码逻辑解释
- flatMap的正确用法:对每个订单,先在用户列表里筛选出
user_id匹配的记录,再把每个匹配的用户和订单合并成新的Map,最后通过flatMap把这些合并后的Map放到同一个流里。 - Map合并:用
HashMap的构造器先复制订单的所有键值对,再调用putAll()添加用户的内容——如果两个Map有重复键(比如这里的user_id),用户的键值对会覆盖订单的,但因为是内连接,这个键的值是相同的,所以不会有问题。 - 空安全处理:用
Objects.equals()代替直接equals(),避免其中一方为null时抛出空指针异常。
可选优化:处理键冲突
如果你的两个Map可能存在相同键但不同值的情况,可以给键加前缀区分,比如:
.map(user -> { Map<String, String> mergedMap = new HashMap<>(); // 订单键加前缀 order.forEach((k, v) -> mergedMap.put("order_" + k, v)); // 用户键加前缀 user.forEach((k, v) -> mergedMap.put("user_" + k, v)); return mergedMap; })
内容的提问来源于stack exchange,提问作者nirvair
相关产品推荐
相关产品推荐

