使用 Java Streams 将映射列表转换为列表映射

Vin*_*lur 3 java dictionary list java-stream

我想将地图列表转换为列表地图。保证所有映射都具有相同的键。

例如,下面的列表

[{key1: val1, key2: val2}, {key1: val3, key2: val4}]
Run Code Online (Sandbox Code Playgroud)

转换为

{key1: [val1, val3], key2: [val2, val4]}
Run Code Online (Sandbox Code Playgroud)

下面的代码使用 for 循环

List<Map<String, Object>> data = getData();

Map<String, List<Object>> result = new HashMap<>();

for (Map<String, Object> element: data) {
    element.forEach((key, val) -> {
        if (result.containsKey(key))
            result.get(key).add(val);
        else
            result.put(key, Arrays.asList(val));
    });
}
Run Code Online (Sandbox Code Playgroud)

我如何才能使用流实现相同的结果?

gur*_*oso 6

例如像这样,依靠java流的收集阶段。

List<Map<String, Object>> data = new ArrayList<>();

HashMap<String, List<Object>> transposed = data.stream()
        .flatMap(i -> i.entrySet().stream())
        .collect(Collectors.groupingBy(
                Map.Entry::getKey,
                HashMap::new,
                Collectors.mapping(Map.Entry::getValue,
                        Collectors.toCollection(ArrayList::new))));
Run Code Online (Sandbox Code Playgroud)

在 SO 上有许多这样的应用程序(例如这个),尽管不是针对这个例子精确定制的。


Vik*_*how 5

这个版本依赖stream.reduce应该做到:

// some values for testing
List<Map<String, Object>> list = new ArrayList<>();
list.add(Map.of("1", "One", "2", "Two"));
list.add(Map.of("3", "Three", "4", "Four", "5", "Five"));
list.add(Map.of("3", "Teen", "5", "Penta"));
list.add(Map.of("3", "Trice", "4", "Quattro"));

// conversion with stream reduction
Map<String, List<Object>> result = list.stream().reduce(new HashMap<>(),
        (acc, map) -> {
            map.forEach((k, v) -> acc.computeIfAbsent(k,
                    l -> new ArrayList<Object>()).add(v));
            return acc;
        },
        (acc1, acc2) -> {
            acc1.putAll(acc2);
            return acc1;
        });

// output
System.out.println(result);
//{1=[One], 2=[Two], 3=[Three, Teen, Trice], 4=[Four, Quattro], 5=[Five, Penta]}
Run Code Online (Sandbox Code Playgroud)

说明:此代码遍历列表中的每个映射并将其累积在哈希映射中(reduce 函数的第一个参数),如果第一次看到键,则累积函数(第二个参数)向哈希映射添加一个新列表(否则它会累积在已经存在的列表中);第三个函数是串行或并行运行流函数之间的一致性所必需的(它在并行运行中合并部分结果)。


Fed*_*ner 5

您可以在没有流的情况下以现代方式完成此操作,如下所示:

Map<String, List<Object>> result = new HashMap<>();
data.forEach(element -> 
    element.forEach((k, v) - > 
        result.computeIfAbsent(k, x -> new ArrayList<>()).add(v)));
Run Code Online (Sandbox Code Playgroud)

这会迭代列表,然后迭代每个映射,并用于Map.computeIfAbsent容纳映射中的result条目。