简体   繁体   中英

groupingBy classifier with several keys

I have a List<Set<Integer>> and want a Map<Integer, List<Set<Integer>>> which will map each Integer to all sets containing that Integer .

The obvious candidate here would be to use Collectors.groupingBy but it doesn't work because groupingBy only allows a classifier function that returns one key. I would need to return several keys for each Set .

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

Here is one example of what I want to achieve without using streams:

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

This will output the following:

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

Two approaches:

Make a stream of all integers in all sets, then associate an integer with all sets which contain it.

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

Create an intermediate maps which map the elements of the set to itself, then merge those entries into the final map.

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

Note that your for loop variant is unnecessarily complicated. Instead of getOrDefault plus putIfAbsent , you can use a single computeIfAbsent :

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

which allows a simplification to a chained operation:

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

An equivalent Stream operation would be:

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

Just do the nested iteration using 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);
    });
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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