简体   繁体   English

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

[英]Convert a list of maps to a map of lists using Java Streams

I would like to convert a list of maps to a map of lists.我想将地图列表转换为列表地图。 It is guaranteed that all the maps have the same keys.保证所有映射都具有相同的键。

For example, the following list例如,下面的列表

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

gets converted to转换为

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

The below code does it using for loops下面的代码使用 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));
    });
}

How would I be able to achieve the same result using streams?我如何才能使用流实现相同的结果?

Eg like this, relying on the collect stage of java streams.例如像这样,依靠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))));

There are a number of such application on SO (eg this one ), though not precisely tailored to this example.在 SO 上有许多这样的应用程序(例如这个),尽管不是针对这个例子精确定制的。

This version relying on stream.reduce should do it:这个依赖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]}

Explanation: this code goes through each map in the list and accumulates it in the hashmap (1st parameter to the reduce function), with the accumulating function (2nd parameter) adding a new list to the hashmap if it sees a key for the first time (otherwise it accumulates in the list already present);说明:此代码遍历列表中的每个映射并将其累积在哈希映射中(reduce 函数的第一个参数),如果第一次看到键,则累积函数(第二个参数)向哈希映射添加一个新列表(否则它会累积在已经存在的列表中); the third function is required for consistency between running the stream function serially or in parallel (it combines partial results in parallel runs).第三个函数是串行或并行运行流函数之间的一致性所必需的(它在并行运行中合并部分结果)。

You could do it without streams, yet in a modern fashion, as follows:您可以在没有流的情况下以现代方式执行此操作,如下所示:

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

This iterates the list and then each map and uses Map.computeIfAbsent to accomodate entries in the result map.这将迭代列表,然后迭代每个映射,并使用Map.computeIfAbsent来容纳result映射中的条目。

You can use Collectors.toMap method:您可以使用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]}

See also: Convert a map of lists into a list of maps另请参阅:将列表映射转换为映射列表

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM