Java Stream Collectors.toMap值是一个Set

时间:2016-10-05 19:55:03

标签: java java-8 java-stream

我想使用Java Stream来运行POJO列表,例如下面的列表List<A>,并将其转换为Map Map<String, Set<String>>

例如,A类是:

class A {
    public String name;
    public String property;
}

我编写了下面的代码,将值收集到地图Map<String, String>

final List<A> as = new ArrayList<>();
// the list as is populated ...

// works if there are no duplicates for name
final Map<String, String> m = as.stream().collect(Collectors.toMap(x -> x.name, x -> x.property));

但是,因为可能有多个POJO具有相同的name,所以我希望地图的值为Set。同一个密钥property的所有name字符串都应该放在同一个字符集中。

如何做到这一点?

// how do i create a stream such that all properties of the same name get into a set under the key name
final Map<String, Set<String>> m = ???

4 个答案:

答案 0 :(得分:19)

groupingBy完全符合您的要求:

import static java.util.stream.Collectors.*;
...
as.stream().collect(groupingBy((x) -> x.name, mapping((x) -> x.property, toSet())));

答案 1 :(得分:2)

@Nevay的回答绝对是使用groupingBy的正确方法,但toMap也可以通过添加mergeFunction作为第三个参数来实现:

as.stream().collect(Collectors.toMap(x -> x.name, 
    x -> new HashSet<>(Arrays.asList(x.property)), 
    (x,y)->{x.addAll(y);return x;} ));

此代码将数组映射到地图,其中密钥为x.name,值为HashSet,其中一个值为x.property。当存在重复的键/值时,然后调用第三个参数merger函数来合并两个HashSet。

PS。如果您使用Apache Common库,您也可以使用他们的SetUtils::union作为合并

答案 2 :(得分:0)

此外,您可以使用Collectors.toMap函数的合并功能选项 Collectors.toMap(keyMapper,valueMapper,mergeFunction)如下:

final Map<String, String> m = as.stream()
                                .collect(Collectors.toMap(
                                          x -> x.name, 
                                          x -> x.property,
                                          (property1, property2) -> property1+";"+property2);

答案 3 :(得分:0)

相同但不同

Map<String, Set<String>> m = new HashMap<>();

as.forEach(a -> {
    m.computeIfAbsent(a.name, v -> new HashSet<>())
            .add(a.property);
    });