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

3
我希望将一个映射列表转换为一个列表映射。保证所有的映射都有相同的键。
例如,以下列表
[{key1: val1, key2: val2}, {key1: val3, key2: val4}]

转换为

{key1: [val1, val3], key2: [val2, val4]}

以下代码使用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));
    });
}

我如何使用流(streams)实现相同的结果?

4个回答

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))));

在 Stack Overflow 上有许多类似的应用程序(例如这个),虽然不是完全针对此示例量身定制。


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]}

解释:此代码遍历列表中的每个映射,并将其累积到哈希映射(reduce函数的第一个参数)中,累加函数(第二个参数)会在第一次看到某个键时向哈希映射添加新的列表(否则它会在已经存在的列表中进行累加操作)。第三个函数用于使串行或并行运行流函数时结果保持一致性(对并行运行的部分结果进行合并)。


5

你可以不使用流(Streams),而采用现代方式来实现,具体如下:

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

该代码迭代列表和每个映射,并使用Map.computeIfAbsentresult map中添加条目。


1
您可以使用 Collectors.toMap 方法:
List<Map<String, String>> listOfMaps = List.of(
        Map.of("key1", "val1", "key2", "val2"),
        Map.of("key1", "val3", "key2", "val4"));

Map<String, List<String>> mapOfLists = listOfMaps.stream()
        // Stream<Map<String,String>>
        .flatMap(map -> map.entrySet().stream())
        // Stream<Map.Entry<String,String>>
        .collect(Collectors.toMap(
                // key
                Map.Entry::getKey,
                // value
                e -> List.of(e.getValue()),
                // merge function
                (list1, list2) -> {
                    List<String> list = new ArrayList<>();
                    list.addAll(list1);
                    list.addAll(list2);
                    return list;
                }, // map factory
                LinkedHashMap::new));

System.out.println(listOfMaps);
// [{key1=val1, key2=val2}, {key1=val3, key2=val4}]
System.out.println(mapOfLists);
// {key1=[val1, val3], key2=[val2, val4]}

另请参阅:将列表地图转换为地图列表


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接