2016-07-13 9 views
4

Ich habe einen List<Set<Integer>> und eine Map<Integer, List<Set<Integer>>> wollen, die jede Integer allen sets abbildet, dass Integer enthält.groupingBy Klassifikator mit mehreren Schlüsseln

Der offensichtliche Kandidat hier wäre Collectors.groupingBy zu verwenden, aber es funktioniert nicht, da groupingBy nur eine Klassifiziererfunktion ermöglicht, die einen Schlüssel zurückgibt. Ich müsste für jede Set mehrere Schlüssel zurückgeben.

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

Hier ist ein Beispiel dafür, was ich ohne Ströme erreichen wollen:

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

Dies wird Ausgang der folgende:

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

Antwort

3

zwei Ansätze:

a Stellen stream aller Ganzzahlen in allen Mengen, dann verknüpfen Sie eine ganze Zahl mit allen Mengen, die es enthalten.

Erstellen Sie eine Zwischenkarte, die die Elemente der Gruppe auf sich selbst abbildet, und fügen Sie diese Einträge dann in die endgültige Karte ein.

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

I wie Ihre erste Annäherung am meisten, und ich bin gespannt, wie es mit der Wiederholung mit 'forEach 'wie in meiner Antwort zu tun vergleicht. – Roland

0

tun Sie einfach die verschachtelte Iteration mit 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); 
    }); 
}); 
+0

Kein Bedarf, für ein Iterable zu streamen. –

1

Beachten Sie, dass Ihre for Schleife Variante unnötig kompliziert. Statt getOrDefault Plus putIfAbsent, können Sie ein verwenden einzelne computeIfAbsent:

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

, die eine Vereinfachung zu einer verketteten Betrieb ermöglicht:

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

Eine äquivalente Stream-Operation sein würde:

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