внутреннее соединение в Java с использованием потоков - PullRequest
4 голосов
/ 30 марта 2019

У меня есть два списка карты:

Заказы

[
    {
        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

Я хочу сделать sql-эквивалентное внутреннее объединение в этом Списке Карт, используя Streams.

Я пробовал это:

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

Это дает мне результат size size (users) * size (orders), который равен 9. И сбор имеет orders.

Но я хочу, чтобы обе карты были объединены в одну, а затем создать из нее список.

На данный момент не может использоваться ни одна библиотека.

1 Ответ

5 голосов
/ 30 марта 2019

Предполагая, что у вас нет повторяющихся записей (по ключу столбца слияния), вы можете использовать такой метод для слияния.

Это создает карту ключа 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());
}
...