java java-8 java-stream data-generation

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



java-8 java-stream (6)

Estoy tratando de generar instancias de Order usando la API Stream. Tengo una función de fábrica que crea el pedido, y se utiliza un DoubleStream para inicializar el importe 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<Order> orderStream = Stream.generate(() -> { return createOrder(); }); orderStream.limit(10).forEach(System.out::println);

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

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

TIA

Viejo


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

Pero, después de todo, esto no es complicado cuando elimina todos sus intentos de almacenar resultados intermedios. Su código completo se puede expresar 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);


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

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

Y cambia tu forma de obtener el doble de esta manera

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

Entonces funciona normalmente.

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


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<Order> createOrders(int numberOfOrders) { List<Order> orders = new ArrayList<>(); doubleStream.limit(numberOfOrders).forEach((value) -> { Order order = new Order(value); orders.add(order); }); return orders; }

¡Gracias de nuevo!

Viejo


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

Se debe operar una secuencia (invocando una operación de secuencia intermedia o terminal) solo una vez . Esto excluye, por ejemplo, las secuencias "bifurcadas", donde la misma fuente alimenta dos o más canalizaciones, o múltiples recorridos de la misma secuencia. Una implementación de flujo puede arrojar IllegalStateException si detecta que el flujo se está reutilizando .

Y en su código, usa la transmisión dos veces (una en createOrder() y la otra cuando .limit().forEach()


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

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


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

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

Desde el Javadoc de Random#doubles(double, double)

Se genera un valor doble 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 tratar de obtenerlo de un 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 reutilizar el método nextDouble , puede incorporar los valores 50.0 y 200.0 .