Was sind in Java die Vorteile von Streams gegenüber Schleifen? [geschlossen]

133

Ich wurde dies bei einem Interview gefragt und bin nicht überzeugt, dass ich die beste Antwort gegeben habe, die ich haben konnte. Ich erwähnte, dass Sie eine parallele Suche durchführen können und dass Nullwerte auf eine Weise behandelt wurden, an die ich mich nicht erinnern konnte. Jetzt merke ich, dass ich an Optionals gedacht habe. Was fehlt mir hier? Sie behaupten, es sei besser oder prägnanter, aber ich bin mir nicht sicher, ob ich damit einverstanden bin.


Wenn man bedenkt, wie prägnant es beantwortet wurde, scheint dies doch keine allzu weit gefasste Frage zu sein.


Wenn sie diese Frage in Interviews stellen und dies eindeutig sind, welchen Zweck könnte es haben, sie aufzuschlüsseln, außer es schwieriger zu machen, eine Antwort zu finden? Ich meine, wonach suchst du? Ich könnte die Frage aufschlüsseln und alle Unterfragen beantworten lassen, aber dann eine übergeordnete Frage mit Links zu allen Unterfragen erstellen ... scheint allerdings ziemlich dumm zu sein. Wenn wir schon dabei sind, geben Sie mir bitte ein Beispiel für eine weniger breite Frage. Ich kenne keine Möglichkeit, nur einen Teil dieser Frage zu stellen und trotzdem eine aussagekräftige Antwort zu erhalten. Ich könnte genau die gleiche Frage auf andere Weise stellen. Zum Beispiel könnte ich fragen: "Welchen Zweck erfüllen Streams?" oder "Wann würde ich einen Stream anstelle einer for-Schleife verwenden?" oder "Warum sich mit Streams anstatt mit for-Schleifen beschäftigen?" Dies sind jedoch alle genau die gleiche Frage.

... oder wird es als zu weit gefasst angesehen, weil jemand eine wirklich lange Mehrpunktantwort gegeben hat? Ehrlich gesagt kann jeder, der es weiß, das mit praktisch jeder Frage tun. Wenn Sie beispielsweise einer der Autoren der JVM sind, könnten Sie wahrscheinlich den ganzen Tag über für Schleifen sprechen, wenn die meisten von uns dies nicht konnten.

"Bitte bearbeiten Sie die Frage, um sie auf ein bestimmtes Problem mit genügend Details zu beschränken, um eine angemessene Antwort zu finden. Vermeiden Sie es, mehrere unterschiedliche Fragen gleichzeitig zu stellen. Weitere Informationen zur Klärung dieser Frage finden Sie auf der Seite" Wie man Fragen stellt "."

Wie unten erwähnt, wurde eine angemessene Antwort gegeben, die beweist, dass es eine gibt und dass es einfach genug ist, sie bereitzustellen.

user447607
quelle
7
Dies ist imho meinungsbasiert. Persönlich bevorzuge ich Streams, weil dadurch der Code besser lesbar wird. Es ermöglicht zu schreiben, was Sie wollen, anstatt wie . Darüber hinaus ist es total übel, erstaunliche Dinge mit Einzeilern zu machen.
Arnaud Denoyelle
18
Auch wenn es eine 30er Linie ist, ein Liner? Ich mag keine langen Ketten.
user447607
1
Außerdem suche ich hier nur die passende Antwort für ein Interview. Dies ist die einzige "Meinung", die zählt.
user447607
1
Aus pädagogischer Sicht hat mir diese Frage auch bei einem zukünftigen Interview eine gewisse Verschlechterung erspart. @Slim hat es wirklich verstanden, aber industriell gesehen spricht sie auch davon, wie Microsoft-Programmiersprachen ihre Karriere darauf aufgebaut haben, die Java-Sprache abzureißen, und schließlich rächt sich Java durch Abreißen Aus dem Lambda-Ausdruck und den Streams der Gegner können wir sehen, was Java in Zukunft mit Strukturen und Gewerkschaften tun wird :)
ShayHaned,
5
Beachten Sie, dass Streams nur einen Bruchteil der Leistung in der funktionalen Programmierung nutzen: - /
Thorbjørn Ravn Andersen

Antworten:

251

Interessant, dass in der Interviewfrage nach den Vorteilen gefragt wird, ohne nach Nachteilen zu fragen, denn es gibt beides.

Streams sind deklarativer . Oder ein ausdrucksstärkerer Stil. Es kann als besser angesehen werden, Ihre Absicht im Code zu deklarieren, als zu beschreiben, wie es gemacht wird:

 return people
     .filter( p -> p.age() < 19)
     .collect(toList());

... sagt ganz klar, dass Sie übereinstimmende Elemente aus einer Liste filtern, während:

 List<Person> filtered = new ArrayList<>();
 for(Person p : people) {
     if(p.age() < 19) {
         filtered.add(p);
     }
 }
 return filtered;

Sagt "Ich mache eine Schleife". Der Zweck der Schleife ist tiefer in der Logik vergraben.

Streams sind oft knapp . Das gleiche Beispiel zeigt dies. Terser ist nicht immer besser, aber wenn Sie gleichzeitig knapp und ausdrucksstark sein können, umso besser.

Streams haben eine starke Affinität zu Funktionen . Java 8 führt Lambdas und funktionale Schnittstellen ein, die eine ganze Reihe leistungsstarker Techniken öffnen. Streams bieten die bequemste und natürlichste Möglichkeit, Funktionen auf Objektsequenzen anzuwenden.

Streams fördern eine geringere Veränderlichkeit . Dies hängt in gewisser Weise mit dem Aspekt der funktionalen Programmierung zusammen. Die Art von Programmen, die Sie mit Streams schreiben, ist in der Regel die Art von Programmen, bei denen Sie keine Objekte ändern.

Streams fördern eine lockerere Kopplung . Ihr Stream-Handling-Code muss weder die Quelle des Streams noch dessen mögliche Beendigungsmethode kennen.

Streams können ein recht ausgefeiltes Verhalten prägnant ausdrücken . Beispielsweise:

 stream.filter(myfilter).findFirst();

Könnte auf den ersten Blick so aussehen, als würde der gesamte Stream gefiltert und dann das erste Element zurückgegeben. Tatsächlich wird jedoch findFirst()der gesamte Vorgang gesteuert, sodass er nach dem Auffinden eines Elements effizient gestoppt wird.

Streams bieten Raum für zukünftige Effizienzgewinne . Einige Leute haben Benchmarking durchgeführt und festgestellt, dass Single-Threaded-Streams von In-Memory- ListS oder Arrays langsamer sein können als die entsprechende Schleife. Dies ist plausibel, da mehr Objekte und Gemeinkosten im Spiel sind.

Aber Streams skalieren. Neben der integrierten Unterstützung von Java für parallele Stream-Operationen gibt es einige Bibliotheken für die verteilte Kartenreduzierung, bei denen Streams als API verwendet werden, da das Modell passt.

Nachteile?

Leistung : Eine forSchleife durch ein Array ist sowohl hinsichtlich der Heap- als auch der CPU-Auslastung extrem leicht. Wenn rohe Geschwindigkeit und Sparsamkeit des Speichers Priorität haben, ist die Verwendung eines Streams schlechter.

Vertrautheit. Die Welt ist voll von erfahrenen prozeduralen Programmierern mit vielen Sprachhintergründen, für die Schleifen bekannt und Streams neu sind. In einigen Umgebungen möchten Sie Code schreiben, der dieser Art von Person vertraut ist.

Kognitiver Overhead . Aufgrund seiner deklarativen Natur und der zunehmenden Abstraktion von dem, was darunter geschieht, müssen Sie möglicherweise ein neues mentales Modell erstellen, wie sich Code auf die Ausführung bezieht. Eigentlich müssen Sie dies nur tun, wenn etwas schief geht oder wenn Sie die Leistung oder subtile Fehler gründlich analysieren müssen. Wenn es "einfach funktioniert", funktioniert es einfach.

Debugger verbessern sich, aber selbst jetzt, wenn Sie in einem Debugger durch den Stream-Code gehen, kann dies schwieriger sein als die entsprechende Schleife, da eine einfache Schleife sehr nahe an den Variablen und Code-Positionen liegt, mit denen ein herkömmlicher Debugger arbeitet.

schlank
quelle
4
Ich denke, es wäre fair zu ordnen, dass Stream-ähnliches Zeug immer häufiger wird und jetzt in vielen häufig verwendeten Sprachen auftaucht, die nicht besonders FP-orientiert sind.
Casey
5
Angesichts der hier aufgeführten Vor- und Nachteile denke ich, dass Streams sich für nichts anderes als sehr einfache Zwecke (wenig Logik, wenn / dann / sonst nicht viele verschachtelte Aufrufe oder Lambdas usw.) in nicht kritischen Leistungsteilen
Henrik Kjus Alstad
1
@HenrikKjusAlstad Das ist absolut nicht der Imbiss, den ich kommunizieren wollte. Streams sind ausgereift, leistungsstark, ausdrucksstark und für Code in Produktionsqualität vollständig geeignet.
schlank
Oh, ich wollte nicht, dass ich es nicht in der Produktion verwenden würde. Stattdessen würde ich standardmäßig altmodische Schleifen / Wenns usw. anstelle von Streams verwenden, insbesondere wenn der resultierende Stream komplex aussehen würde. Ich bin mir sicher, dass es Anwendungen gibt, bei denen ein Stream Schleifen schlägt und wenn dies klar ist, aber meistens denke ich, dass er "gebunden" ist oder manchmal sogar umgekehrt. Daher würde ich das kognitive Overhead-Argument für das Festhalten an den alten Methoden in den Vordergrund stellen.
Henrik Kjus Alstad
1
@lijepdam - aber Sie hätten immer noch Code mit der Aufschrift "Ich iteriere über diese Liste (siehe in der Schleife, um herauszufinden, warum)", wenn "Iterieren über die Liste" nicht die Kernabsicht des Codes ist.
schlank
16

Abgesehen von dem syntaktischen Spaß sind Streams so konzipiert, dass sie mit potenziell unendlich großen Datenmengen arbeiten können, während Arrays, Sammlungen und fast jede Java SE-Klasse, die Iterable implementiert, vollständig im Speicher gespeichert sind.

Ein Nachteil eines Streams ist, dass Filter, Zuordnungen usw. keine aktivierten Ausnahmen auslösen können. Dies macht einen Stream zu einer schlechten Wahl für beispielsweise Zwischen-E / A-Operationen.

VGR
quelle
7
Natürlich können Sie auch unendliche Quellen durchlaufen.
schlank
Wie verwenden Sie Streams, wenn die zu verarbeitenden Elemente in einer Datenbank beibehalten werden? Ein Junior-Entwickler könnte versucht sein, alle in einer Sammlung zu lesen, nur um Streams zu verwenden. Und das wäre eine Katastrophe.
Lluis Martinez
2
@LluisMartinez Eine gute DB-Client-Bibliothek gibt so etwas wie zurück Stream<Row>- oder es wäre möglich, eine eigene StreamImplementierung zu schreiben , die DB-Ergebniscursoroperationen umschließt.
schlank
Dass Streams Ausnahmen stillschweigend ignorieren, ist ein Fehler, von dem ich kürzlich gebissen wurde. Unintuitiv.
xxfelixxx
@xxfelixxx Streams ignorieren Ausnahmen nicht stillschweigend. Versuchen Sie Arrays.asList("test", null).stream().forEach(s -> System.out.println(s.length()));
Folgendes
8
  1. Sie haben falsch erkannt: Paralleloperationen verwenden Streams, nicht Optionals.

  2. Sie können Methoden definieren, die mit Streams arbeiten: Nehmen Sie sie als Parameter, geben Sie sie zurück usw. Sie können keine Methode definieren, die eine Schleife als Parameter verwendet. Dies ermöglicht eine einmalige und mehrfache Verwendung eines komplizierten Stream-Vorgangs. Beachten Sie, dass Java hier einen Nachteil hat: Ihre Methoden müssen someMethod(stream)im Gegensatz zu den eigenen Methoden aufgerufen werden. Das stream.someMethod()Mischen dieser Methoden erschwert das Lesen: Versuchen Sie, die Reihenfolge der Operationen in anzuzeigen

    myMethod2(myMethod(stream.transform(...)).filter(...))

    Viele andere Sprachen (C #, Kotlin, Scala usw.) erlauben irgendeine Form von "Erweiterungsmethoden".

  3. Selbst wenn Sie nur sequentielle Operationen benötigen und diese nicht wiederverwenden möchten, sodass Sie entweder Streams oder Schleifen verwenden können, können einfache Operationen an Streams recht komplexen Änderungen in den Schleifen entsprechen.

Alexey Romanov
quelle
Erklären 1. Ist die optionale Schnittstelle nicht das Mittel, mit dem Nullen in Ketten behandelt werden? In Bezug auf 3 ist dies sinnvoll, da bei kurzgeschlossenen Filtern die Methode nur für bestimmte Vorkommen aufgerufen wird. Effizient. Es ist sinnvoll, dass ich sagen könnte, dass ihre Verwendung die Notwendigkeit verringert, zusätzlichen Code zu schreiben, der getestet werden muss usw. Nach Überprüfung bin ich mir nicht sicher, was Sie mit dem sequentiellen Fall in 2
meinen
1. Optionalist eine Alternative zu null, hat aber nichts mit parallelen Operationen zu tun. Es sei denn "Jetzt ist mir klar, dass ich an Optionals gedacht habe" in Ihrer Frage geht es nur um die nullHandhabung?
Alexey Romanov
Ich habe die Reihenfolge 2 und 3 geändert und beide etwas erweitert.
Alexey Romanov
6

Sie durchlaufen eine Sequenz (Array, Sammlung, Eingabe, ...), weil Sie eine Funktion auf die Elemente der Sequenz anwenden möchten.

Streams bieten Ihnen die Möglichkeit , Funktionen für Sequenzelemente zusammenzustellen und die gängigsten Funktionen (z. B. Zuordnung, Filterung, Suche, Sortierung, Sammlung usw.) unabhängig von einem konkreten Fall zu implementieren .

Daher können Sie bei einer Schleifenaufgabe in den meisten Fällen diese mit weniger Code mithilfe von Streams ausdrücken, dh Sie erhalten Lesbarkeit .

wero
quelle
4
Nun, es ist jedoch nicht nur Lesbarkeit. Code, den Sie nicht schreiben müssen, ist Code, den Sie nicht testen müssen.
user447607
3
Anscheinend haben Sie auch gute Antworten für Ihr Interview
wero
6

Ich würde sagen, seine Parallelisierung ist so einfach zu bedienen. Versuchen Sie, über Millionen von Einträgen parallel zu einer for-Schleife zu iterieren. Wir gehen zu vielen CPUs, nicht schneller; Je einfacher es ist, parallel zu laufen, desto besser und mitStream s ist dies ein Kinderspiel.

Was ich sehr mag, ist die Ausführlichkeit, die sie bieten. Es braucht wenig Zeit, um zu verstehen, was sie tatsächlich tun und produzieren, im Gegensatz dazu, wie sie es tun.

Eugene
quelle