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
.