【问题标题】:Java 8 Stream IllegalStateException: Stream has already been operated on or closedJava 8 Stream IllegalStateException:流已经被操作或关闭
【发布时间】:2015-01-16 18:29:30
【问题描述】:

我正在尝试使用 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 创建一个随机量时,会抛出异常。

【问题讨论】:

    标签: java java-8 java-stream data-generation


    【解决方案1】:

    答案在Stream(我的重点)的javadoc中:

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

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

    【讨论】:

      【解决方案2】:

      正如在其他答案中所说,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);
      

      【讨论】:

      • 哇 - 非常优雅 - 我喜欢它!
      • 当我尝试它时 - Eclipse 说:“Order 类型的构造对象与描述符的返回类型 double 不兼容”。想法?
      • 我昨天很着急,因此疏忽,map 必须是 mapToObj,因为它将类型从原始类型 double 更改为引用类型,修复它。
      【解决方案3】:

      As fge states,您不能(不应该)多次使用Stream

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

      来自Random#doubles(double, double)的Javadoc

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

      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

      【讨论】:

        【解决方案4】:

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

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

        再次感谢!

        奥莱

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

          • 真是太甜了!谢谢!
          • Hm...当我运行 createOrders(10) 时,我仍然得到 IllegalStateException: stream has already been operating on or closed.
          • 没关系 - 测试中有问题。很好的解决方案!
          • 好的 - 我知道问题出在哪里。如果我在同一个方法中多次调用 createOrders,我会得到异常。关于如何解决这个问题的任何想法?再次感谢。
          • 好的 - 搞清楚了。每次调用都必须创建一个新的随机数。所以: private List createOrders(int numberOfOrders) { return new Random().doubles(50, 200).limit(numberOfOrders).mapToObj(Order::new).collect(Collectors.toList()); }
          【解决方案6】:

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

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

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

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

          然后就可以正常使用了。

          Stream has already been operated upon or closed Exception的帖子中找到这种方式

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-04-13
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多