Gemäß der Dokumentation auf der Oracle-Website :
Nebenwirkungen von Verhaltensparametern bei Stream-Vorgängen werden im Allgemeinen nicht empfohlen, da sie häufig zu unwissentlichen Verstößen gegen die Anforderungen an die Staatenlosigkeit sowie zu anderen Sicherheitsrisiken für Threads führen können.
Umfasst dies das Speichern von Elementen des Streams in einer Datenbank?
Stellen Sie sich den folgenden (Pseudo-) Code vor:
public SavedCar saveCar(Car car) {
SavedCar savedCar = this.getDb().save(car);
return savedCar;
}
public List<SavedCars> saveCars(List<Car> cars) {
return cars.stream()
.map(this::saveCar)
.collect(Collectors.toList());
}
Was sind die unerwünschten Auswirkungen dieser Implementierung:
public SavedCar saveCar(Car car) {
SavedCar savedCar = this.getDb().save(car);
return savedCar;
}
public List<SavedCars> saveCars(List<Car> cars) {
List<SavedCars> savedCars = new ArrayList<>();
for (Cat car : cars) {
savedCars.add(this.saveCar(car));
}
return savedCars.
}
for
Schleife?parallelStream
, würden Sie sicherlich den Transaktionskontext verlieren.Antworten:
Dieser Link ist für Java 8. Möglicherweise möchten Sie die Dokumentation für Java 9 (veröffentlicht im Jahr 2017) und spätere Versionen lesen, da diese diesbezüglich expliziter sind. Speziell:
Und auch die aktualisierte Version des von Ihnen zitierten Dokuments:
Alle Betonung von mir.
Wie Sie sehen können, wird in der aktuellen offiziellen Dokumentation detaillierter auf die Probleme eingegangen, die auftreten können, wenn Sie sich entscheiden, Nebenwirkungen in Ihren Stream-Vorgängen zu verwenden. Es ist auch ganz klar auf
forEach
undforEachOrdered
die einzigen Terminalbetrieb zu sein , wo die Ausführung von Nebenwirkungen garantiert wird (wohlgemerkt, fadenSicherheitsFragen immer noch gelten, wie die offiziellen Beispiele zeigen).Davon abgesehen und in Bezug auf Ihren spezifischen Code und nur diesen Code:
Ich sehe keine Streams-bezogenen Probleme mit dem Code wie er ist.
.map()
Schritt wird ausgeführt, weil.collect()
(eine veränderbare Reduktionsoperation , die das offizielle Dokument anstelle von Dingen empfiehlt.forEach(list::add)
) auf.map()
der Ausgabe beruht und da diese Ausgabe (dhsaveCar()
die Ausgabe) sich von ihrer Eingabe unterscheidet, kann der Stream nicht "beweisen" dass [eliding] es das Ergebnis der Berechnung nicht beeinflussen würde " .parallelStream()
so, dass es keine Parallelitätsprobleme verursachen sollte, die zuvor nicht existierten (natürlich.parallel()
kann es zu Problemen kommen , wenn jemand später eine hinzufügt - ähnlich wie wenn jemand beschlossen hat, einefor
Schleife zu parallelisieren , indem er neue Threads für die inneren Berechnungen startet ).Das bedeutet nicht, dass der Code in diesem Beispiel Good Code ™ ist. Die Sequenz
.stream.map(::someSideEffect()).collect()
als Möglichkeit, Nebenwirkungen für jedes Element in einer Sammlung auszuführen, sieht möglicherweise einfacher / kurzer / eleganter aus. als seinfor
Gegenstück, und es kann manchmal sein. Wie Eugene, Holger und einige andere Ihnen sagten, gibt es jedoch bessere Möglichkeiten, dies zu erreichen.Ein kurzer Gedanke: Die Kosten für das
Stream
Starten eines einfachenfor
oder das Iterieren eines einfachen sind nicht zu vernachlässigen, es sei denn, Sie haben viele Elemente, und wenn Sie viele Elemente haben, möchten Sie: a) wahrscheinlich keinen neuen DB-Zugriff vornehmen für jedensaveAll(List items)
wäre also eine API besser; und b) wahrscheinlich nicht den Leistungseinbruch der Verarbeitung viel ertragen wollen von Elementen nacheinander, so dass Sie am Ende die Parallelisierung verwenden und dann eine ganze Reihe neuer Probleme auftreten.quelle
Das absolut einfachste Beispiel ist:
In diesem Fall wird ab Java-9
map
nicht mehr ausgeführt. da braucht man es gar nicht, um das zu wissencount
.Es gibt mehrere andere Fälle, in denen Nebenwirkungen Ihnen große Schmerzen verursachen würden. unter bestimmten Bedingungen.
quelle
count()
würde noch ausgeführt werden, aber die Implementierung kann Zwischenschritte überspringen, wenn sie das Ergebnis aus der Quelle erzeugen kann (aber es gibt viele ifs auf Implementierungsebene )Collector
.work
auf alten Java 8-Versionen, aber nicht auf Java 9 oder höher wäre. Diese spezielle Optimierung für Streams mit Größe wurde