Java 8 Stream IllegalStateException:流已被操作或关闭

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/27990451/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 12:46:10  来源:igfitidea点击:

Java 8 Stream IllegalStateException: Stream has already been operated on or closed

javajava-8java-streamdata-generation

提问by Ole

I'm trying to generate Order instances using the Stream API. I have a factory function that creates the order, and a DoubleStream is used to initialize the amount of the order.

我正在尝试使用 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<Order> orderStream = Stream.generate(() -> {
    return createOrder();
});

orderStream.limit(10).forEach(System.out::println);

If I initialize the Order instance using a literal (1.0), this works fine. When I use the doubleStream to create a random amount, the exception is thrown.

如果我使用文字 (1.0) 初始化 Order 实例,则效果很好。当我使用 doubleStream 创建一个随机数量时,会抛出异常。

回答by fge

The answer is in the javadoc of Stream(emphases mine):

答案在Stream(强调我的)的 javadoc 中:

A stream should be operated on (invoking an intermediate or terminal stream operation) only once. This rules out, for example, "forked" streams, where the same source feeds two or more pipelines, or multiple traversals of the same stream. A stream implementation may throw IllegalStateException if it detects that the stream is being reused.

一个流应该只被操作一次(调用一个中间或终端流操作)。例如,这排除了“分叉”流,其中相同的源提供两个或多个管道,或者同一流的多次遍历。如果流实现检测到流正在被重用,它可能会抛出 IllegalStateException

And in your code, you do use the stream twice (once in createOrder()and the other usage when you .limit().forEach()

在您的代码中,您确实使用了两次流(一次createOrder()使用,另一次使用时.limit().forEach()

回答by Holger

As said in other answers, Streams are single-use items and you have to create a new Streameach time you need one.

正如其他答案中所说,Streams 是一次性物品,Stream每次需要时都必须创建一个新物品。

But, after all, this isn't complicated when you remove all your attempts to store intermediate results. Your entire code can be expressed as:

但是,毕竟,当您删除所有存储中间结果的尝试时,这并不复杂。您的整个代码可以表示为:

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);

or even simpler

甚至更简单

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

回答by Sotirios Delimanolis

As fge states, you can't (shouldn't) consume a Streammore than once.

正如 fge 所述,您不能(不应)Stream多次使用 a 。

Any idea how to fix this?

知道如何解决这个问题吗?

From the Javadoc of Random#doubles(double, double)

来自 Javadoc Random#doubles(double, double)

A pseudorandom double value is generated as if it's the result of calling the following method with the origin and 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 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;
}

Implement such a method and use it to get a new doublevalue each time you need one instead of trying to get it from a DoubleStream. Possibly use a DoubleSupplier.

实现这样的方法并在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;
}

If you're not going to reuse the nextDoublemethod, you can inline the values 50.0and 200.0.

如果您不打算重用该nextDouble方法,则可以内联值50.0200.0

回答by Ole

Thank you - that was very helpful. I also came up with a different implementation that works well for now:

谢谢 - 这很有帮助。我还提出了一个目前运行良好的不同实现:

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;
}

Thanks again!

再次感谢!

Ole

奥莱

回答by user2336315

Your method could be a one-liner like this instead. You need to use mapToObj, not map

您的方法可能是这样的单线。您需要使用mapToObj,而不是map

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

回答by David Pham

You should use Supplier function interface for your initialization like this

您应该像这样使用供应商函数接口进行初始化

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

And change your way to get double like this

并改变你的方式来获得这样的双倍

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

Then it works normally.

然后它正常工作。

Found this way from the post Stream has already been operated upon or closed Exception

从帖子中发现这种方式Stream has been operation on or closed Exception