Wie kann ich machen das kartesische Produkt mit Java-8-streams?
Habe ich die folgende Auflistung geben:
Map<String, Collection<String>> map;
Ich würde gerne erstellen Sie einzigartige Kombinationen von jeweils map.size()
aus einem einzigen Wert in der Auflistung für jeden Schlüssel.
Zum Beispiel nehmen wir an, die Karte sieht wie folgt aus:
A, {a1, a2, a3, ..., an}
B, {b1, b2, b3, ..., bn}
C, {c1, c2, c3, ..., cn}
Das Ergebnis würde ich gerne bekommen würde eine List<Set<String>>
Ergebnis der Suche ähnelt (Reihenfolge ist nicht wichtig, es muss nur ein "komplettes" Ergebnis aus allen möglichen Kombinationen):
{a1, b1, c1},
{a1, b1, c2},
{a1, b1, c3},
{a1, b2, c1},
{a1, b2, c2},
{a1, b2, c3},
...
{a2, b1, c1},
{a2, b1, c2},
...
{a3, b1, c1},
{a3, b1, c2},
...
{an, bn, cn}
Dies ist im Grunde ein zählen-problem, aber ich möchte, um zu sehen, ob eine Lösung möglich ist, mit Java-8-streams.
- Haben Sie Sorge, über die Tasten in der Karte?
- Technisch, das Ergebnis ist definiert als mit mehreren
class ResultEntry {String key, value;}
, so dass endgültige Ergebnis ist von TypList<Set<ResultEntry>>
, also ja, ich kümmere mich um den Schlüssel, aber ich dachte, wenn ich die Grundlagen der permutation, die ich herausfinden kann, wie man die Schlüssel in der Zuordnung. - Sie haben ein bisschen ein problem, dass die map-keys sind nicht geordnet, es sei denn, es ist ein
SortedMap
. Dies bedeutet, dass Sie nicht wissen, ob Sie Ihre Antwort beginnen mit{a1,b1,c1}
oder{c1,a1,b1}
oder eine der anderen 4 Möglichkeiten. Die Frage ist immer noch interessant, aber ohne Anordnung der Tasten, es ist nicht eine Frage, würde nützlich sein im wirklichen Leben. MitList<Collection<String>>
als input vielleicht mehr Sinn machen. - Du hast Recht, es könnte sein, eine Liste (da durch die Vereinfachung des Problems mehr zu Kern), aber die Reihenfolge nicht wirklich wichtig ist (Frage aktualisiert). Ich zeigte eine geordnete Ergebnismenge zu verstärken, dass es nur eine Zählung problem. Aber, was noch wichtiger ist, dass die Menge der Permutationen ist "komplett".
- Ich denke, es ist nicht als "Permutationen"? Wahrscheinlich "Kombinationen"? Muttersprachler, was denkst du?
- Ich bin nicht native, aber: Kombinationen und Permutationen
- Dieses problem ist nicht über Permutationen oder Kombinationen. Es sieht mehr wie ein Kartesisches Produkt.
- Ich kann sehen, von Alex ' Kommentaren, die er ursprünglich wollte-Kombinationen. Das zurückgegebene Ergebnis wird benötigt, um eine
List<Set<String>>
(nichtStream
), und die Elemente einerSet<String>
erlauben nicht die für die Permutationen wie{a1, b2, a1, ...}
. Im Einklang mit dieser Abstraktion, es gibt dem Benutzer (also Alex) die Freiheit, um zusätzliche Logik zu beschneiden bestimmte Kombinationen, wenn der Algorithmus und das Ergebnis ist in seiner generischen form, so dass ich denke, es war ein Zufall, dass dieser entpuppte sich als ein Kartesisches Produkt auch.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Lösen Sie dieses mit dem rekursiven
flatMap
Kette.Erste, was wir brauchen, hin und her bewegen durch die map-Werte, es ist besser, kopieren Sie die
ArrayList
(dies ist nicht der Tiefe Kopie, in Ihrem Fall ist esArrayList
3 Elemente, so dass der zusätzliche Speicherverbrauch ist niedrig).Zweiten, zu erhalten ein Präfix der zuvor besuchten Elemente, erstellen wir einen Helfer unveränderlich
Prefix
Klasse:Dies ist sehr einfach, unveränderlich, Link-Liste, die verwendet werden können, wie diese:
Als Nächstes erstellen wir die interne Methode, die Ketten flatMaps:
Sieht aus wie Rekursion, aber es ist noch komplexer: es nicht nennen sich selbst direkt, sondern bestanden lambda fordert die äußeren Methode. Parameter:
List
von der ursprünglichen Werte (new ArrayList<>(map.values)
in Ihrem Fall).null
wennoffset == 0
). Es enthält die aktuell ausgewählten Elemente aus den Sammlungenlist.get(0)
,list.get(1)
bis zulist.get(offset-1)
.Wenn wir erreichten das Ende der Werte-Liste (
offset == values.size() - 1
), wir bilden die Elemente der letzten Sammlung von den Werten, die endgültige Kombination mit den Lieferanten. Ansonsten verwenden wir dieflatMap
für jeden intermediate element vergrößert das Präfix, und fordert diecomb
Methode wieder für die nächsten offset.Schließlich hier public Methode um diese Funktion zu verwenden:
Ein Beispiel:
Sammeln wir die einzelnen Kombinationen auf die
LinkedHashSet
wieder zum beibehalten der Reihenfolge. Sie kann auch jede andere Sammlung statt (z.B.ArrayList::new
).Einer Lösung, die vor allem arbeitet Sie auf Listen, die Dinge viel einfacher. Es macht einen rekursiven Aufruf in
flatMap
verfolgen der Elemente, die bereits kombiniert, und die Sammlungen von Elementen, die noch fehlen, und bietet das Ergebnis dieser geschachtelten rekursiven Konstruktion als ein Strom von Listen:flatMap
, also eine dedizierte benchmark interessant sein könnte hier. Abgesehen davon, ich denke, die streams sind im Allgemeinen nicht die beste Lösung ist hier (aber genau das war gefragt). Sollte man überlegen, ein Iterierbar für die Lösung dieses ...Kartesische Produkt in Java 8 mit forEach:
Hier ist eine andere Lösung, die nicht so viele features von
Streams
als Tagir das Beispiel, aber ich glaube, dass es mehr straight-forward:Können Sie entfernen Sie die
Sorted
- Präfix wenn Sie nicht interessiert sind, in der Reihenfolge der Elemente; obwohl, ich glaube, es ist einfacher zu Debuggen, wenn alles sortiert ist.Verwendung:
Genießen!
Collection.forEach
ist nicht ein Teil des Stream-API). Sie können ersetzen.forEach
mit der guten altenfor-in
Schleife und der code werden Java-5-kompatibel. Beachten Sie auch, dass Sie speichern alle Kombinationen in den Speicher. Dies scheint zwar ok für die OP, kann es problematisch werden, mit größeren Eingang. Schließlich gibt es keine einfache Weise zu parallelisieren es.Während es ist nicht eine Stream-Lösung, Guave com.google.common.sammeln.Stellt dies für Sie übernimmt
Eine einfachere Antwort, für eine einfachere situation, wo Sie nur wollen, um das kartesische Produkt der Elemente der beiden Sammlungen.
Hier ist etwas code, der verwendet
flatMap
zu erzeugen das kartesische Produkt von zwei kurzen Listen:Wenn Sie weitere hinzufügen möchten Sammlungen, einfach verschachteln der streams einen Wurf weiter:
Verwenden Sie ein Verbraucher-Funktion der Klasse, einer Liste und einer foreach -
In der Schleife, erstellen Sie kombinierte Liste
Schrieb ich eine Klasse Umsetzung
Iterable
, und halten nur das aktuelle Element im Speicher. DieDurchsuchbar
sowie dieIterator
umgewandelt werden können, um eineStream
wenn gewünscht.