2014-02-06 10 views
14

Ich habe vor kurzem begonnen, mit Java 8 zu spielen, nachdem ich in Haskell/Scala schon einiges getan habe. Ich versuche, mit Funktionen höherer Ordnung in Java wie map oder forEach zu spielen, und ich kämpfe, um zu verstehen, welche Motivation es war, alles in Richtung Stream Ideologie zu schieben. Ich verstehe, es ist schön, allgemeine Abstraktion gibt, wird angenommen, faul sein, aber lassen Sie sich ein sehr einfaches, gemeinsames Beispiel betrachten:Java 8 - stream Ideologie

list.map(x -> do_sth(x)); 

sehr häufig Idiom, das ein List<T> zurückzukehren erwarten. Nun, in Java 8, ich brauche etw dieser Art tun:

list.stream().map(x -> doSth(x)).collect(Collectors.toList()) 

Nun, so weit ich das sehen, wird der Strom nicht die Karte gilt bis collect genannt wird, so wird es einen Durchlauf durch die sein Sammlung unter der Haube. Was ich nicht sehen kann ist, warum die üblichen Anwendungsfälle für Karten, Listen wie map.toList(), list.groupBy() nicht zu entsprechenden Schnittstellen hinzugefügt werden? Gibt es eine Designentscheidung, die mir hier fehlt?

Antwort

24

Eine Handvoll neuer Methoden wurde direkt zu verschiedenen Sammlungen hinzugefügt, die eifrig mutative Operationen an diesen Sammlungen durchführen. Um beispielsweise eine Funktion für jedes Element einer Liste auszuführen und die ursprünglichen Elemente durch die Rückgabewerte zu ersetzen, verwenden Sie List.replaceAll(UnaryOperator). Andere Beispiele hierfür sind Collection.removeIf(Predicate), List.sort() und Map.replaceAll(BiFunction).

Im Gegensatz dazu gibt es eine Menge neuer Methoden wie Filter, Map, Skip, Limit, Sort, Distinct usw., die zum Stream gehören und die Quelle nicht mutieren, sondern Elemente stromabwärts übergeben . Wir haben darüber nachgedacht, diese direkt den Sammlungen hinzuzufügen. An diesem Punkt entstanden mehrere Fragen. Wie unterscheiden wir die eifrigen, mutativen Operationen von den faulen, stromerzeugenden Operationen? Das Überladen ist schwierig, da sie unterschiedliche Rückgabetypen haben und daher unterschiedliche Namen haben müssen. Wie würden solche Operationen angekettet werden? Die eifrigen Operationen müßten Sammlungen erzeugen, um Zwischenergebnisse zu speichern, was möglicherweise ziemlich teuer ist. Die resultierende Sammlungs-API hätte eine verwirrende Mischung aus eifrigen, mutativen und faulen, nicht-mutierenden Methoden.

Eine zweite Erwägung berücksichtigt mögliche Inkompatibilitäten beim Hinzufügen von Standardmethoden. Das größte Risiko beim Hinzufügen von Standardmethoden zu einer Schnittstelle ist ein Namenskonflikt mit einer vorhandenen Methode für eine Implementierung dieser Schnittstelle. Wenn eine Methode mit demselben Namen und denselben Argumenten (oft keine Argumente) einen anderen Rückgabetyp hat, ist das eine unvermeidbare Inkompatibilität. Aus diesem Grund haben wir ziemlich zögerlich viele Standardmethoden hinzugefügt.

Aus diesen Gründen haben wir uns dafür entschieden, die faulen, nicht mutierenden Methoden innerhalb der Stream-API auf Kosten der zusätzlichen Methodenaufrufe stream() und collect() zur Überbrückung zwischen Sammlungen und Streams zu halten. Für einige häufige Fälle haben wir eifrige, mutierende Aufrufe direkt zu den Auflistungsschnittstellen hinzugefügt, wie die oben aufgeführten.

Weitere Informationen finden Sie unter lambdafaq.org.

+1

Was ich nicht verstehe und vielleicht können Sie erklären - warum dies, .collect (Collectors.toList()), wenn es eine ToList() -Methode eingebaut haben könnte? Es scheint mir nur sehr ausführlich und unhandlich zu sein. –

+5

Wenn Sie 'toList' haben, werden Sie wahrscheinlich auch' toSet' und 'toMap' wollen. Aber Sie würden immer noch die "Collect" -Methode wollen, weil sie so flexibel ist. So hätten jetzt einige Sammelvorgänge ihre eigenen dedizierten Convenience-Methoden, während für andere Sie "collect" aufrufen müssten. Vielleicht wäre die Bequemlichkeit die Unordentlichkeit und Widersprüchlichkeit wert, aber die Designer dachten nicht. –

+0

Ich sah, dass viele Codes geschrieben werden müssen: stream() ... collect (Collectors.toList()), anstelle von stream() ... toList() wegen einiger dummer Gedanken von JDK-Designer. Das ist ist ekelhaft. "toList", "toSet", "toMap" sind die am häufigsten verwendeten Beendigungsoperationen. Sie sollten der Stream API hinzugefügt werden. –