Java-Streams vs Iteratoren

7

Ich habe mit dem neuen und glänzenden funktionalen Teil von Java gespielt und eines der Dinge, die mich am meisten verwirren sind Streams?

Was nützen sie?

Bei Google habe ich meistens Erklärungen gefunden, wie man sie benutzt, und praktische Beispiele, die ich bereits verstanden habe, nichts konkretes über die Magie hinter den Kulissen, was mich interessiert.

Ich meine nicht im praktischen Sinne, aus einigen funktionalen Sprachen kommend habe ich map / filter / reduce / etc gefunden. ziemlich schnell, aber warum müssen wir zuerst in einen Stream konvertieren? Java hat bereits Iteratoren. Gibt es einen fundamentalen Unterschied zwischen Stream und Iterator, als wäre man faul und der andere nicht? Oder ist es etwas anderes?

Fazit: Was ist der grundlegende Unterschied zwischen Iteratoren und Streams und welche Funktionalität konnte nicht als Erweiterung von Iteratoren implementiert werden und benötigte eine ganze neue Typenfamilie?

    
Rares Dima 17.02.2018, 22:23
quelle

5 Antworten

8

Über Streams im Allgemeinen zu sprechen, ist ein großes Thema. Ich werde jedoch ableiten, warum Sie die Streams-API gegenüber Iteratoren bevorzugen sollten.

In erster Linie können wir mit der Stream-API jetzt auf einer viel höheren Abstraktionsebene programmieren, genau wie bei SQL-Abfragen, d. h. wir drücken aus, was wir wollen, und überlassen der Bibliothek den Rest.

Zweitens führen Stream-Operationen ihre Iterationen hinter den Kulissen durch (interne Iteration), das heißt, die Verarbeitung der Daten könnte parallel oder in einer anderen Reihenfolge erfolgen, die optimaler sein könnte.

Wenn Sie andererseits entscheiden, Ihre Sammlung explizit zu durchlaufen, um Berechnungen durchzuführen, sei es mit einem Iterator oder dem syntaktischen Zucker für einen Iterator (die erweiterte for-Schleife), dann nehmen Sie die Elemente in der Sammlung explizit und sie einzeln verarbeiten, also ist es inhärent seriell.

Die Verwendung von Iteratoren anstelle der Stream-API bedeutet auch, dass viel mehr Arbeit getan werden muss, wenn Sie parallel vorgehen oder verschiedene Möglichkeiten zur Optimierung Ihres Programms finden möchten.

Das bedeutet aber auch, dass Sie viel mehr Zeit mit den Details auf der unteren Ebene verbringen, anstatt sich nur darauf zu konzentrieren, was Ihr Programm tun soll.

Auch im Buch Java-8 in Action erwähnt:

  

Die interne Iteration in der Streams-Bibliothek kann automatisch auswählen   eine Datendarstellung und Implementierung der Parallelität zu Ihrem   Hardware. Im Gegensatz dazu, wenn Sie die externe Iteration einmal gewählt haben   für jeden schreiben, dann haben Sie sich im Wesentlichen verpflichtet, selbst zu managen   Parallelität. (Selbstverwaltung bedeutet in der Praxis entweder "einen schönen Tag"   Wir werden das parallelisieren oder den langen und mühsamen Kampf beginnen   mit Aufgaben und synchronisiert ".)

     

Java 8 benötigte eine Schnittstelle wie Collection, aber ohne Iteratoren, ergo   Stream!

Mit der stream-API ist Ihr Leben im Wesentlichen viel einfacher, aber was ich am nützlichsten finde, ist die Tatsache, dass Sie jetzt mehr Zeit darauf verwenden können, sich auf das zu konzentrieren, was Ihr Code tun soll und gleichzeitig auf Sie kann entscheiden, parallel zu gehen, ohne sich mit dem Low-Level-Zeug zu beschäftigen.

Das heißt natürlich nicht, Streams immer und überall zu verwenden. Vielmehr werden die Vorteile der Verwendung von Streams über Iteratoren hervorgehoben.

Es gibt bestimmte Orte, wo es geeigneter ist, Iteratoren als die Stream-API zu verwenden und umgekehrt. Wählen Sie also mit Bedacht, mit welcher Vorgehensweise Daten in Sammlungen verarbeitet werden sollen.

    
Aominè 17.02.2018 22:51
quelle
5

Das Hinzufügen von Stream Methoden zu einem vorhandenen Iterator war definitiv eine Möglichkeit, da Standardimplementierungen für alle zusätzlichen Methoden zur Verfügung gestellt werden konnten, aber diese API Änderung hat erhebliche Nachteile:

  • Ihre Streams werden mit Iteratoren "verheiratet", selbst in Situationen, in denen Sie keinen Iterator (z. B. einen Generator-Stream) möchten
  • Sie erhalten keine Streams von Collections "kostenlos" - genauso wie Sie stream aufrufen, müssten Sie iterator aufrufen (was bei erweiterten Schleifen mit Iteratoren bereits der Fall ist)
  • Sie benötigen immer noch eine Menge neuer Typen für primitive Streams, weil es in Iteratoren kein ähnliches Konzept gibt. Das ist die Funktionalität, die schwer auf Iteratoren zu "graft".

Der Gesamtgewinn durch das Zusammenführen von Streams mit Iteratoren sah nicht nach viel aus, daher sieht es so aus, als ob die API-Designer einen Ansatz für ein sauberes Blatt gewählt hätten.

    
dasblinkenlight 17.02.2018 22:48
quelle
4
  

Gibt es einen grundlegenden Unterschied zwischen Stream und Iterator?   faul sein und der andere nicht? Oder ist es etwas anderes?

Ja, der grundlegende Unterschied besteht darin, dass Streams intern verarbeitet werden. Was wir sagen, wenn wir einen Stream laufen lassen, ist, dass wir all dieses Zeug wollen, um in dieses Zeug zu filtern, und geben Sie uns dieses Ergebnis. Wir sagen natürlich nicht, wie wir das wollen. Dies bedeutet, dass derselbe Quellcode später parallel auf einer Grafikkarte oder in einer noch unbekannten Weise ausgeführt werden könnte. Wir wollen nur, dass das passiert.

Es gibt viele interessante Dinge, die hinter den Kulissen passieren können, wenn wir als Programmierer explizit auf die Kriterien eingehen, die uns nicht interessieren. Dies ist auch ein großer Teil der Funktion hinter den funktionalen Schnittstellen und einigen Lambda-Ausdrücken. Die Idee ist, dass, wenn wir sagen, dass uns das zunächst egal ist, die Compiler das Problem lösen können, anstatt es zu lösen. Manchmal können verschiedene Computeranordnungen die Dinge auf eine andere Weise besser lösen, wie eine bessere Parallelisierung.

  

Unterste Zeile: Was ist der grundlegende Unterschied zwischen Iteratoren und   Streams und welche Funktionalität konnte nicht als Erweiterung implementiert werden   zu Iteratoren und brauchte eine ganz neue Familie von Typen?

Iteratoren sagten, wie das Problem gelöst werden müsse. Es musste dieses Element dann dieses Element dann dieses Element und der Compiler kann nicht wissen, ob es einen tiefen und scheinbar versteckten Grund dafür gibt, anstatt auf irgendeine andere Weise. Streams sagen, dass es dir egal ist, iterate vorwärts rückwärts, auf tausend verschiedenen Prozessoren, auf der GPU, ist es egal.

Ich möchte, dass jedes Element auf diese Weise verarbeitet wird. Ich möchte ein Element nach dem anderen auf diese Weise verarbeiten. Letzteres ist eigentlich unnötig restriktiv.

    
Tatarize 17.02.2018 23:06
quelle
4

In meinen Augen sind Java 8-Streams konzeptionell sehr ähnlich wie Unix-Pipes. Sie beginnen mit bestimmten Daten, die Sie filtern, bearbeiten oder Aktionen ausführen, bis Sie das gewünschte Ergebnis erhalten. Der resultierende Code ist auch viel weniger ausführlich, als es mit herkömmlichen Konstrukten möglich wäre. Andere haben bereits erwähnt, dass Streams über das was im Gegensatz zum wie stehen.

Ein besonderes Beispiel für meine Arbeit ist das Scrappen von Websites. Die JSoup-Bibliothek macht das Ergebnis einer CSS-Abfrage als einen Typ verfügbar, der Collection implementiert. Wenn Sie es jetzt in Java 8 verwenden, erhalten Sie Streams kostenlos.

Das bedeutet, dass Sie bestimmte Tags mit einer CSS-Abfrage auswählen, bestimmte Tags, die Sie nicht interessieren, herausfiltern, in ein Objekt konvertieren und in eine Liste einfügen: all dies in nur wenigen Zeilen.

Es ist durchaus möglich, dies in Java 7 zu tun, aber Sie müssen die Liste deklarieren, die Tags durchlaufen, eine bedingte Anweisung haben, das Objekt instanziieren, es der Liste hinzufügen ... Es ist leicht möglich, drei Mal zu haben so viele Zeilen.

Die Vorteile, sich nicht mit der Implementierung zu beschäftigen, sind alle gültig und korrekt, aber in einem eher geschäftsorientierten Ansatz macht es Ihren Code viel lesbarer und daher einfacher zu warten, auch von anderen Leuten. Chefs so.

Die Kehrseite ist, dass aufgrund der konservativen Herangehensweise von Java an neue Funktionen eine Menge von syntaktischem Zucker die Implementierung verbirgt, bis zu dem Moment, an dem Sie einen Exception-Stack-Trace erhalten und Sie wünschen, dass Sie englische Literatur studiert haben statt Software-Engineering.

    
SeverityOne 18.02.2018 00:25
quelle
1

Zusätzlich zu den anderen Antworten werde ich auch eine zynischere Antwort geben. In den meisten Fällen hat es damit zu tun, wie oft die Programmierer tippen müssen und wie präzise der Code aussieht. Viele Sprachen unterstützen bereits Lambdas und Streams. Und die Leute, die in diesen Sprachen schreiben, sagen Zeug wie "Java saugt, weil du all diesen Code schreiben musst, um alle Elemente in einer Liste zu verarbeiten. Meine Sprache unterstützt funktionale Programmierung und deshalb ist sie besser als Java." Java benötigt keine Streams oder Lambdas, es geht ohne sie gut. Aber es muss wettbewerbsfähig bleiben. Es gibt eine Menge Java-Programmierer da draußen und wir mögen es nicht, wenn unsere Sprache in den Schmutz gezogen wird. Und ich stimme zu, dass sie zwar keine Streams benötigen, aber es macht Spaß, sie zu schreiben. Streams führen am Ende zu weniger Tipparbeit und Sie erledigen Ihre Arbeit viel schneller. Es ist glatt und hübsch.

Es gibt ein Problem mit Streams und Lambdas, die möglicherweise von Zeit zu Zeit wiederholt werden müssen. Und das ist, dass Lambdas mit finalen Objekten und endgültigen Primitiven arbeiten, das heißt, die Objekte und Primitiven, die außerhalb des Lambda sind, müssen endgültig sein. In den meisten Fällen sind Sie vielleicht in der Lage, dies zu umgehen, aber ab und zu können Sie einfach zum Iterieren zurückkehren, weil es einfacher ist. Mach dir keine Sorgen, wenn es passiert, wirst du es sehen, weil dein Code nicht kompiliert wird.

    
Jose Martinez 18.02.2018 00:32
quelle