Según la documentación en el sitio web de Oracle :
En general, se desaconsejan los efectos secundarios en los parámetros de comportamiento para transmitir las operaciones, ya que a menudo pueden conducir a violaciones involuntarias del requisito de apatridia, así como a otros riesgos de seguridad de hilos.
¿Esto incluye guardar elementos de la secuencia en una base de datos?
Imagine el siguiente (pseudo) código:
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());
}
¿Cuáles son los efectos no deseados opuestos a esta implementación?
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
bucle regular ?parallelStream
, ciertamente perdería el contexto de la transacción.Respuestas:
Ese enlace es para Java 8. Es posible que desee leer la documentación para Java 9 (que salió en 2017) y versiones posteriores, ya que son más explícitos a este respecto. Específicamente:
Y también la versión actualizada del documento que citó:
Todo el énfasis es mío.
Como puede ver, la documentación oficial actual entra en más detalles sobre los problemas que puede encontrar si decide usar efectos secundarios en sus operaciones de transmisión. También es muy claro
forEach
yforEachOrdered
es la única operación de terminal en la que se garantiza la ejecución de efectos secundarios (tenga en cuenta que los problemas de seguridad de subprocesos aún se aplican, como lo muestran los ejemplos oficiales).Dicho esto, y con respecto a su código específico, y solo dicho código:
No veo problemas relacionados con Streams con dicho código tal como está.
.map()
paso se ejecutará porque.collect()
(una operación de reducción mutable , que es lo que recomienda el documento oficial en lugar de cosas como.forEach(list::add)
) se basa en.map()
la salida y, dado que esta (es decirsaveCar()
, la salida) es diferente de su entrada, el flujo no puede "probar que [elidándose] no afectaría el resultado del cálculo " .parallelStream()
por lo que no debería presentar ningún problema de concurrencia que no existiera anteriormente (por supuesto, si alguien agregó.parallel()
más tarde, entonces pueden surgir problemas, como si alguien decidiera paralelizar unfor
bucle activando nuevos hilos para los cálculos internos )Eso no significa que el código en ese ejemplo sea Good Code ™. ¿La secuencia
.stream.map(::someSideEffect()).collect()
como una forma de realizar operaciones de efectos secundarios para cada elemento de una colección puede parecer más simple / corta / elegante? que sufor
contraparte, y a veces puede ser. Sin embargo, como Eugene, Holger y algunos otros le dijeron, hay mejores maneras de abordar esto.Como una idea rápida: el costo de activar un
Stream
vs iterar un simplefor
no es insignificante a menos que tenga muchos artículos, y si tiene muchos artículos, entonces: a) probablemente no quiera hacer un nuevo acceso a la base de datos para cada uno, entonces unasaveAll(List items)
API sería mejor; y b) probablemente no quieren tomar el impacto en el rendimiento del procesamiento de una gran cantidad de elementos de forma secuencial, por lo que terminaría usando la paralelización y luego surgiría un nuevo conjunto de problemas.fuente
El ejemplo más sencillo es:
En este caso, desde java-9 en adelante,
map
no se ejecutará; ya que no lo necesitas para saber elcount
, en absoluto.Hay otros casos múltiples en los que los efectos secundarios le causarían mucho dolor; bajo ciertas condiciones.
fuente
count()
que aún se ejecutará, pero la implementación puede omitir los pasos intermedios si puede producir el resultado de la fuente (pero hay muchos ifs a nivel de implementación )Collector
.work
en versiones antiguas de Java 8 pero no en Java 9 o posterior. Esa optimización particular para transmisiones de tamaño se introdujo en JDK-8067969