9

我正在尝试使用 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);

如果我使用文字 (1.0) 初始化 Order 实例,则可以正常工作。当我使用 doubleStream 创建随机数量时,会引发异常。

4

6 回答 6

15

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

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

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

于 2015-01-16T18:36:24.363 回答
4

如其他答案所述,Streams 是一次性物品,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);
于 2015-01-16T19:59:09.440 回答
1

正如 fge 所说,您不能(不应该)消耗 aStream超过一次。

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

来自 JavadocRandom#doubles(double, double)

生成一个伪随机双精度值,就好像它是使用原点和绑定调用以下方法的结果:

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方法,则可以内联值50.0200.0.

于 2015-01-16T18:45:35.303 回答
0

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

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

再次感谢!

奥莱

于 2015-01-16T20:36:05.700 回答
0

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

private List<Order> createOrders(int numberOfOrders) {
     return doubleStream.limit(numberOfOrders).mapToObj(Order::new).collect(Collectors.toList());
}
于 2015-01-16T21:55:36.173 回答
0

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

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

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

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

然后就可以正常工作了。

从帖子中以这种方式发现Stream 已被操作或关闭 Exception

于 2017-11-20T10:34:50.940 回答