groupingBy分类器有几个键

时间:2016-07-13 08:26:31

标签: java java-stream

我有一个List<Set<Integer>>,想要一个Map<Integer, List<Set<Integer>>>,它会将每个Integer映射到包含sets的所有Integer

这里显而易见的候选人将使用Collectors.groupingBy,但它不起作用,因为groupingBy只允许返回一个键的分类器函数。我需要为每个Set返回几个键。

List<Set<Integer>> setList ...;
Map<Integer, List<Set<Integer>>> setMap = setList.stream().collect(
                groupingBy(eachSet -> ))  

以下是我想要在不使用流的情况下实现的一个示例:

List<Set<Integer>> setList ...
System.out.print("setList: ");                                                              
System.out.println(setList);                                                                

Map<Integer, List<Set<Integer>>> setMap = new HashMap<>();                                  
for(Set<Integer> eachSet: setList) {                                                        
    for(Integer i: eachSet) {                                                               
        List<Set<Integer>> newSetList = setMap.getOrDefault(i, new ArrayList<>());          
        newSetList.add(eachSet);                                                            
        setMap.putIfAbsent(i, newSetList);                                                  
    }                                                                                       
}                                                                                           
System.out.print("setMap: ");                                                               
System.out.println(setMap);                                                                 

这将输出以下内容:

setList: [[1], [1], [1, 3], [2, 3, 7]]
setMap: {1=[[1], [1], [1, 3]], 2=[[2, 3, 7]], 3=[[1, 3], [2, 3, 7]], 7=[[2, 3, 7]]}

3 个答案:

答案 0 :(得分:3)

两种方法:

在所有集合中生成所有整数的流,然后将整数与包含它的所有集合相关联。

    Map<Integer, List<Set<Integer>>> m1 =
            list.stream().flatMap(Set::stream)
                .distinct()
                .collect(
                        Collectors.toMap(a -> a,
                                b -> list.stream().filter(set -> set.contains(b)).collect(Collectors.toList()))
                );

创建一个中间地图,将集合的元素映射到自身,然后将这些条目合并到最终地图中。

    Map<Integer, List<Set<Integer>>> m2 = list.stream()
                .map(x -> x.stream().collect(Collectors.toMap(a -> a, b -> x)))
                .flatMap(x -> x.entrySet().stream())
                .collect(
                         Collectors.toMap(Map.Entry::getKey,
                         a -> List.of(a.getValue()), (l1, l2) -> Stream.concat(l1.stream(), l2.stream()).collect(Collectors.toList()))
                      );

答案 1 :(得分:1)

请注意,您的for循环变体不必要地复杂化。您可以使用单个getOrDefault

,而不是putIfAbsentcomputeIfAbsent
for(Set<Integer> eachSet: setList) {
    for(Integer i: eachSet) {
        List<Set<Integer>> setList = setMap.computeIfAbsent(i, key -> new ArrayList<>());
        setList.add(eachSet);
    }
}

允许简化链式操作:

for(Set<Integer> eachSet: setList) {
    for(Integer i: eachSet) {
        setMap.computeIfAbsent(i, key -> new ArrayList<>()).add(eachSet);
    }
}

等效的Stream操作将是:

Map<Integer, List<Set<Integer>>> setMap = setList.stream()
    .flatMap(set -> set.stream().map(i -> new AbstractMap.SimpleImmutableEntry<>(i, set)))
    .collect(groupingBy(Map.Entry::getKey, mapping(Map.Entry::getValue, toList())));

答案 2 :(得分:0)

使用forEach

进行嵌套迭代
List<Set<Integer>> setList = ...;
Map<Integer, List<Set<Integer>>> setMap = new HashMap<>();

setList.stream().forEach(val -> { //set<Integer>
    val.stream().forEach(i -> { //Integer
        List<Set<Integer>> newSetList = setMap.getOrDefault(i, new ArrayList<>());
        newSetList.add(val);
        setMap.putIfAbsent(i, newSetList);
    });
});