2016-07-27 25 views
2

ich eine kartesische Produkt Funktion in JavaScript habe:Kartesisches Produkt mit Hilfe von Java-Streams

function cartesianProduct(arr) { 
    return arr.reduce(function(a,b) { 
     return a.map(function(x) { 
      return b.map(function(y) { 
       return x.concat(y); 
      }); 
     }).reduce(function(a,b) { return a.concat(b); }, []); 
    }, [[]]); 
} 

Also, wenn ich eine 3D-Array habe:

var data = [[['D']], [['E'],['L','M','N']]]; 

Das Ergebnis cartesianProduct(data) wäre der 2D-Array:

[['D','E'], ['D','L','M','N']] 

Was ich versuche diesen Wagen zu tun ist, schreiben esian Produktfunktion in Java mit Streams.

Bisher habe ich im Anschluss an die in Java:

public Collection<Collection<String>> cartesianProduct(Collection<Collection<Collection<String>>> arr) { 

    return arr.stream().reduce(new ArrayList<Collection<String>>(), (a, b) -> { 
     return a.stream().map(x -> { 
      return b.stream().map(y -> { 
       return Stream.concat(x.stream(), y.stream()); 
      }); 
     }).reduce(new ArrayList<String>(), (c, d) -> { 
      return Stream.concat(c, d); 
     }); 
    }); 
} 

Ich habe eine Art Fehlerprüfung, die besagt:

ArrayList<String> ist nicht kompatibel mit Stream<Stream<String>>

Meine Vermutungen was falsch ist:

  • Ich brauche irgendwo einen Kollektor zu verwenden (vielleicht nach dem Stream.concat)
  • Der Datentyp für die Identität falsch
+1

Siehe http://stackoverflow.com/questions/32131987/how-can-i-make-cartesian-product-with-java-8-streams oder http://stackoverflow.com/questions/32631602/cartesian -product-of-streams-in-java-8-as-stream-using-streams-only – Tunaki

+1

Was zur Hölle hat das mit dem Cross-Produkt zu tun? – michaelsnowden

+0

Ich glaube nicht, dass ein Stream hier das richtige Modell ist.Ein Stream nimmt Elemente einer einzelnen Liste/Menge/Pool/was auch immer und macht etwas mit ihnen. Aber Sie versuchen, Elemente aus zwei separaten Entitäten zu übernehmen und sie auf eine bestimmte Art zu kombinieren. Sie können Lambda-Ausdrücke verwenden, um alle Ihre JavaScript-Schließungen darzustellen, aber ich denke nicht, dass ein Stream das richtige Werkzeug ist. – Bobulous

Antwort

2

Dies ist möglich, mit einem wenig funktionalen Programmierung Magie. Hier Methode, das Collection<Collection<Collection<T>>> akzeptiert und produziert Stream<Collection<T>>:

static <T> Stream<Collection<T>> cartesianProduct(Collection<Collection<Collection<T>>> arr) 
{ 
    return arr.stream() 
     .<Supplier<Stream<Collection<T>>>> map(c -> c::stream) 
     .reduce((s1, s2) ->() -> s1.get().flatMap(
       a -> s2.get().map(b -> Stream.concat(a.stream(), b.stream()) 
         .collect(Collectors.toList())))) 
     .orElseGet(() ->() -> Stream.<Collection<T>>of(Collections.emptyList())) 
     .get(); 
} 

Anwendungsbeispiel:

cartesianProduct(
    Arrays.asList(Arrays.asList(Arrays.asList("D")), 
     Arrays.asList(Arrays.asList("E"), Arrays.asList("L", "M", "N")))) 
      .forEach(System.out::println); 

Ausgang:

[D, E] 
[D, L, M, N] 

Natürlich statt .forEach() können Sie die Ergebnisse die List sammeln, wenn Sie möchte stattdessen Collection<Collection<T>> zurückgeben, aber zurückgeben Stream siehe ms flexibler für mich.

Ein bisschen Erklärung:

Hier schaffen wir einen Strom von Strom Lieferanten über map(c -> c::stream). Jede Funktion dieses Stroms kann bei Bedarf einen Strom der entsprechenden Sammlungselemente erzeugen. Wir tun dies, weil Streams einmal ablaufen (andernfalls würde ein Stream von Streams ausreichen). Danach reduzieren wir diesen Strom von Lieferanten, die für jedes Paar einen neuen Lieferanten erstellen, der flatMaps two ihre Elemente zu den verketteten Listen streamt und abbildet. Der orElseGet Teil ist notwendig, um den leeren Eingang zu handhaben. Die letzte .get() ruft nur den finalen Stream-Lieferanten auf, um den resultierenden Stream zu erhalten.

Verwandte Themen