Java 8 Stream IllegalStateException:Stream已经被操作或关闭
我正在尝试使用Stream API生成Order实例。 我有一个创建订单的工厂函数,DoubleStream用于初始化订单金额。
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);
如果我使用文字(1.0)初始化Order实例,这可以正常工作。 当我使用doubleStream创建随机数量时,抛出exception。
知道如何解决这个问题吗?
TIA,
奥勒
答案是在Stream
的javadoc(强调我的):
应该只对一个流进行操作(调用中间或终端流操作) 。 例如,这排除了“分叉”流,其中相同的源提供两个或更多个管道,或者同一个流的多个遍历。 如果流实现检测到正在重用流,则它可能会抛出IllegalStateException 。
在您的代码中,您确实使用了两次流(一次在createOrder()
,另一次使用时.limit().forEach()
如其他答案所述, Stream
是一次性项目,每次需要时都必须创建一个新的Stream
。
但是,毕竟,当您删除所有存储中间结果的尝试时,这并不复杂。 您的整个代码可以表示为:
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);
甚至更简单
r.doubles(10, 50.0, 200.0).mapToObj(Order::new).forEach(System.out::println);
正如fge所述 ,你不能(不应该)不止一次地使用Stream
。
知道如何解决这个问题吗?
来自Random#doubles(double, double)
javadoc Random#doubles(double, double)
生成伪随机双精度值,就好像它是使用origin和bound调用以下方法的结果:
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; }
实现这样一个方法,并在每次需要时使用它来获取一个新的double
值,而不是试图从DoubleStream
获取它。 可能使用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; }
如果您不打算重用nextDouble
方法,则可nextDouble
联值50.0
和200.0
。
谢谢 – 这非常有帮助。 我还想出了一个现在运行良好的不同实现:
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; }
再次感谢!
奥勒
你的方法可能是这样的单行代码。 你需要使用mapToObj
,而不是map
private List createOrders(int numberOfOrders) { return doubleStream.limit(numberOfOrders).mapToObj(Order::new).collect(Collectors.toList()); }
您应该像这样使用供应商function界面进行初始化
Supplier> streamSupplier = () -> (new Random().doubles(50.0, 200.0).boxed());
并改变你的方式来获得这样的双倍
streamSupplier.get().findFirst().get()
然后它正常工作。
发现这种方式从后期流已经被操作或关闭exception