Sammeln Sie die Ergebnisse einer Kartenoperation in einer Karte mit Collectors.toMap oder groupingBy

8

Ich habe eine Liste mit Typen List<A>und mit der Kartenoperation eine kollektive Liste mit Typen List<B>für alle A-Elemente, die in einer Liste zusammengeführt sind.

List<A> listofA = [A1, A2, A3, A4, A5, ...]

List<B> listofB = listofA.stream()
  .map(a -> repo.getListofB(a))
  .flatMap(Collection::stream)
  .collect(Collectors.toList());

ohne flatmap

List<List<B>> listOflistofB = listofA.stream()
  .map(a -> repo.getListofB(a))
  .collect(Collectors.toList());

Ich möchte die Ergebnisse als Karte vom Typ sammeln Map<A, List<B>>und bisher mit verschiedenen Collectors.toMapoder Collectors.groupingByOptionen versuchen , aber nicht in der Lage sein, das gewünschte Ergebnis zu erzielen.

Amitoj
quelle
2
Haben Sie AElemente in Ihrem wiederholt List<A>?
Federico Peralta Schaffner

Antworten:

8

Sie können den toMapKollektor mit einer begrenzten Methodenreferenz verwenden, um das zu erhalten, was Sie benötigen. Beachten Sie auch, dass diese Lösung davon ausgeht, dass Sie keine A-Instanzen in Ihrem Quellcontainer wiederholt haben. Wenn diese Voraussetzung erfüllt ist, erhalten Sie mit dieser Lösung das gewünschte Ergebnis. So sieht es aus.

Map<A, Collection<B>> resultMap = listofA.stream()
    .collect(Collectors.toMap(Function.identity(), repo::getListofB);

Wenn Sie doppelte A-Elemente haben, müssen Sie diese Zusammenführungsfunktion zusätzlich zu den oben angegebenen verwenden. Die Zusammenführungsfunktion behandelt etwaige Schlüsselkonflikte.

Map<A, Collection<B>> resultMap = listofA.stream()
       .collect(Collectors.toMap(Function.identity(), repo::getListofB, 
            (a, b) -> {
                a.addAll(b);
                return a;
        }));

Und hier ist ein viel prägnanterer Java9-Ansatz, bei dem der flatMappingKollektor verwendet wird, um wiederholte A-Elemente zu verarbeiten.

Map<A, List<B>> aToBmap = listofA.stream()
        .collect(Collectors.groupingBy(Function.identity(),
                Collectors.flatMapping(a -> getListofB(a).stream(), 
                        Collectors.toList())));
Ravindra Ranwala
quelle
2
Hey, schön, dass du hinzugefügt hast, wie man mit Duplikaten umgeht! Ich habe eine Antwort hinzugefügt, ich werde es zulassen, weil ich denke, dass es Ihre ergänzt, Prost!
Federico Peralta Schaffner
2
Ja, Ihr Kommentar war hilfreich. Guter Fang !
Ravindra Ranwala
3

Es wäre einfach,

listofA.stream().collect(toMap(Function.identity(), a -> getListofB(a)));
Code_Mode
quelle
2

Um zu erfassen, Mapwo Schlüssel die AObjekte unverändert sind und die Werte die Liste der entsprechenden BObjekte sind, können Sie den toList()Kollektor durch den folgenden Kollektor ersetzen :

toMap(Function.identity(), a -> repo.getListOfB(a))

Das erste Argument definiert, wie der Schlüssel aus dem ursprünglichen Objekt berechnet wird : identity()Nimmt das ursprüngliche Objekt des Streams unverändert.

Das zweite Argument definiert, wie der Wert berechnet wird. Hier besteht es also nur aus einem Aufruf Ihrer Methode, der a Ain eine Liste von umwandelt B.

Da die repoMethode nur einen Parameter verwendet, können Sie die Übersichtlichkeit verbessern, indem Sie das Lambda durch eine Methodenreferenz ersetzen:

toMap(Function.identity(), repo::getListOfB)
kgautron
quelle
Danke @kgautron, deine Antwort ist richtig und klar erklärt. Ich habe die andere Antwort akzeptiert, weil ich sie zuerst gesehen habe.
Amitoj
2

In dieser Antwort zeige ich, was passiert, wenn Sie AElemente in Ihrer List<A> listofAListe wiederholt haben .

Wenn Duplikate vorhanden listofAwären, würde der folgende Code ein IllegalStateException:

Map<A, Collection<B>> resultMap = listofA.stream()
        .collect(Collectors.toMap(
                            Function.identity(), 
                            repo::getListofB);

Die Ausnahme wird möglicherweise ausgelöst, da Collectors.toMapnicht weiß, wie sie zusammengeführt werden soll Werte werden sollen, wenn eine Kollision in den Schlüsseln auftritt (dh wenn die Key Mapper-Funktion Duplikate zurückgibt, wie dies der Fall wäre, Function.identity()wenn die listofAListe wiederholte Elemente enthalten würde ).

Dies ist in den Dokumenten klar angegeben :

Wenn die zugeordneten Schlüssel Duplikate enthalten (gemäß Object.equals(Object)), IllegalStateExceptionwird beim Ausführen des Erfassungsvorgangs ein ausgelöst. Wenn die zugeordneten Schlüssel möglicherweise Duplikate enthalten, verwenden SietoMap(Function, Function, BinaryOperator stattdessen).

Die Dokumente geben uns auch die Lösung: Wenn sich Elemente wiederholen, müssen wir eine Möglichkeit zum Zusammenführen von Werten bereitstellen. Hier ist eine solche Möglichkeit:

Map<A, Collection<B>> resultMap = listofA.stream()
        .collect(Collectors.toMap(
                            Function.identity(), 
                            a -> new ArrayList<>(repo.getListofB(a)),
                            (left, right) -> {
                                left.addAll(right);
                                return left;
                            });

Dies verwendet die überladene Version Collectors.toMap, die eine Zusammenführungsfunktion als drittes Argument akzeptiert . Wird innerhalb der Zusammenführungsfunktion Collection.addAllverwendet, um das hinzuzufügenBAWird Elemente jedes wiederholten Elements für jedes Element in eine eindeutige Liste aufzunehmen A.

In der Value Mapper-Funktion wird eine neue ArrayListerstellt, damit das Original List<B>von jeder Anicht mutiert wird. Während wir ein erstellen Arraylist, wissen wir im Voraus, dass es mutiert werden kann (dh wir können später Elemente hinzufügen, falls Duplikate vorhanden sind listofA).

Federico Peralta Schaffner
quelle