【问题标题】:inner join in java using streams使用流在java中进行内部连接
【发布时间】:2019-04-01 17:25:03
【问题描述】:

我有两个 List Map:

订单

[
    {
        item_id=1, 
        item=item-1, 
        user_id=1
    },
    {
        item_id=2, 
        item=item-2, 
        user_id=2
    }, 
    {
        item_id=3, 
        item=item-3, 
        user_id=3
    }
]

用户

[
    {
        user_id=1, 
        name=abh, 
        email=abh@bit.com
    }, 
    {
        user_id=2, 
        name=pol, 
        email=pol@bit.com
    }, 
    {
        user_id=3, 
        name=tre, 
        email=tre@bit.com
    }
]

它们被初始化为

List<Map<String, String>> data

我想使用 Streams 在此 List Maps 上执行 sql 等效内连接。

我试过这个:

List<Map<String, String>> collect = leftData.stream().flatMap(t1 -> rightData.stream())
                .filter(t -> t.get(joinColumnTableLeft).equals(t.get(joinColumnTableRight)))
                .collect(Collectors.toList());

这给了我一个 size(users) * size(orders) 的结果,即 9。 并且收藏有orders

但我希望将两个地图合并为一个,然后从中创建一个列表。

目前无法使用任何库。

【问题讨论】:

  • 它们是如何初始化为List&lt;Map&lt;String, String&gt;&gt; data的,你能提供它的代码吗?
  • 从第二个列表创建一个 Map。然后遍历第一个列表,对于每个元素,使用 Map 按用户 ID 查找用户。你真的应该开始定义类而不是将数据存储到地图中。
  • @JBNizet 我知道,但我正在编写一个通用的 csv 解析器。因此我必须使用地图
  • 好吧,照我说的做,但是使用地图而不是适当的对象。
  • 我不太明白那个.. :/

标签: java java-8 java-stream


【解决方案1】:

假设您没有重复条目(通过合并列键),您可以使用这样的方法进行合并。

这会在其中一个列表中逐行创建一个将 mergeColumn 键映射到完整映射的映射,然后在通过迭代另一个映射进行合并时将其用于查找。

static List<Map<String, String>> merge(List<Map<String, String>> left, 
       List<Map<String, String>> right, String joinColumnTableLeft,
       String joinColumnTableRight) {

    Map<String, Map<String, String>> rightById = right.stream()
            .collect(Collectors.toMap(m -> m.get(joinColumnTableRight), 
                                      Function.identity()));

    return left.stream()
               .filter(e -> rightById.containsKey(e.get(joinColumnTableLeft)))
               .map(l -> {
                 Map<String, String> all = new HashMap<>();
                 all.putAll(l);
                 all.putAll(rightById.get(l.get(joinColumnTableLeft)));

                 return all;
               })
               .collect(Collectors.toList());
}

作为测试:

Map<String, String> left1 = new HashMap<>(), right1 = new HashMap<>();
left1.put("a", "A");
left1.put("b", "B");
left1.put("c", "C");

right1.put("a", "A");
right1.put("d", "B");

Map<String, String> left2 = new HashMap<>(), right2 = new HashMap<>();
left2.put("a", "AA");
left2.put("b", "BB");
left2.put("c", "CC");

right2.put("a", "AA");
right2.put("d", "BB");

System.out.println(merge(Arrays.asList(left1, left2), 
        Arrays.asList(right1, right2), "a", "a"));

输出为:[{a=A, b=B, c=C, d=B}, {a=AA, b=BB, c=CC, d=BB}]

不过,条目的顺序并不重要。请注意,这假设除了连接列之外没有重叠的键。否则,您可能希望收集成对的地图,而不是在新地图上调用 putAll


以下将支持重复的连接键(并将为每个键的所有条目生成笛卡尔积):

static List<Map<String, String>> merge(List<Map<String, String>> left, 
        List<Map<String, String>> right,
        String joinColumnTableLeft, String joinColumnTableRight) {

    Map<String, List<Map<String, String>>> rightById = right.stream()
            .collect(Collectors.groupingBy(m -> m.get(joinColumnTableRight)));

    return left.stream()
            .filter(e -> rightById.containsKey(e.get(joinColumnTableLeft)))
            .flatMap(l -> rightById.get(l.get(joinColumnTableLeft)).stream()
                    .map(r -> {
                                Map<String, String> all = new HashMap<>();
                                all.putAll(l);
                                all.putAll(r);

                                return all;
                            }
                    )
    ).collect(Collectors.toList());
}

【讨论】:

  • 当我在订单中有两次 user_id 时,我得到了IllegalStateException: duplicate key。 user_id=3, user_id=3
猜你喜欢
  • 1970-01-01
  • 2020-02-17
  • 2013-11-19
  • 2014-05-31
  • 2012-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多