Java 8 Stream IllegalStateException: Stream ya se ha operado o cerrado

Intento generar instancias de pedido utilizando Stream API. Tengo una función de fábrica que crea el pedido, y DoubleStream se usa para inicializar el monto del pedido.

private DoubleStream doubleStream = new Random().doubles(50.0, 200.0); private Order createOrder() { return new Order(doubleStream.findFirst().getAsDouble()); } @Test public void test() { Stream orderStream = Stream.generate(() -> { return createOrder(); }); orderStream.limit(10).forEach(System.out::println); 

Si inicializo la instancia Order utilizando un literal (1.0), esto funciona bien. Cuando uso el doubleStream para crear una cantidad aleatoria, se lanza la excepción.

¿Algúna idea de cómo arreglar esto?

TIA,

Viejo

La respuesta está en el javadoc de Stream (énfasis mío):

Una secuencia debe ser operada (invocando una operación de flujo intermedio o terminal) solo una vez . Esto excluye, por ejemplo, las transmisiones “bifurcadas”, donde la misma fuente alimenta dos o más canalizaciones, o múltiples cruces de la misma secuencia. Una implementación de flujo puede lanzar IllegalStateException si detecta que la transmisión se está reutilizando .

Y en su código, usted usa la transmisión dos veces (una vez en createOrder() y el otro uso cuando .limit().forEach()

Como se dijo en otras respuestas, Stream s son elementos de un solo uso y debe crear un nuevo Stream cada vez que lo necesite.

Pero, después de todo, esto no es complicado cuando elimina todos sus bashs de almacenar resultados intermedios. Su código completo se puede express como:

 Random r=new Random(); // the only stateful thing to remember // defining and executing the chain of operations: r.doubles(50.0, 200.0).mapToObj(Order::new).limit(10).forEach(System.out::println); 

o incluso más simple

 r.doubles(10, 50.0, 200.0).mapToObj(Order::new).forEach(System.out::println); 

Como dice fge , no puedes (no deberías) consumir un Stream más de una vez.

¿Algúna idea de cómo arreglar esto?

Del Javadoc de Random#doubles(double, double)

Se genera un doble valor pseudoaleatorio como si fuera el resultado de llamar al siguiente método con el origen y el límite:

 double nextDouble(double origin, double bound) { double r = nextDouble(); r = r * (bound - origin) + origin; if (r >= bound) // correct for rounding r = Math.nextDown(bound); return r; } 

Implemente dicho método y úselo para obtener un nuevo valor double cada vez que lo necesite en lugar de intentar obtenerlo de DoubleStream . Posiblemente use un DoubleSupplier .

 private final Random random = new Random(); private DoubleSupplier supplier = () -> nextDouble(random, 50.0, 200.0); private Order createOrder() { return new Order(supplier.getAsDouble()); } private static double nextDouble(Random random, double origin, double bound) { double r = random.nextDouble(); r = r * (bound - origin) + origin; if (r >= bound) // correct for rounding r = Math.nextDown(bound); return r; } 

Si no va a volver a utilizar el método nextDouble , puede nextDouble los valores 50.0 y 200.0 .

Gracias, eso fue de mucha ayuda. También se me ocurrió una implementación diferente que funciona bien por ahora:

 private DoubleStream doubleStream = new Random().doubles(50.0, 200.0); private List createOrders(int numberOfOrders) { List orders = new ArrayList<>(); doubleStream.limit(numberOfOrders).forEach((value) -> { Order order = new Order(value); orders.add(order); }); return orders; } 

¡Gracias de nuevo!

Viejo

Su método podría ser de una sola línea como este en su lugar. Necesita usar mapToObj , no el map

 private List createOrders(int numberOfOrders) { return doubleStream.limit(numberOfOrders).mapToObj(Order::new).collect(Collectors.toList()); } 

Debe usar la interfaz de función del Proveedor para su inicialización como esta

 Supplier> streamSupplier = () -> (new Random().doubles(50.0, 200.0).boxed()); 

Y cambia tu camino para obtener el doble como este

 streamSupplier.get().findFirst().get() 

Entonces funciona normalmente.

Encontrado de esta manera desde la publicación Stream ya ha sido operado o cerrado Excepción