2017-08-01 91 views
18

我想遍历一个巨大的数组,并执行一组需要很长时间的复杂指令。但是,如果超过30秒过去了,我希望它放弃。Java 8 Streams - 超时?

ex。

final long start = System.currentTimeMillis(); 
myDataStructure.stream() 
    .while(() -> System.currentTimeMillis() <= start + 30000) 
    .forEach(e -> 
    { 
     ... 
    }); 

我想避免只说returnforEach里面调用,如果满足特定条件。

+2

https://stackoverflow.com/questions/41392286/java-8-completablefuture-stream-and-timeouts这里是一个样本回答,也许它可以帮助你。 – utkusonmez

+1

如果您正在计算副作用的变化并在之后应用(假设计算远比应用更昂贵),您可以使用特殊的“收集器”实现,该收集器在达到超时时停止收集。 – SpaceTrucker

+2

相关:https://stackoverflow.com/questions/20746429/limit-a-stream-by-a-predicate –

回答

14

如果在这种情况下迭代流或数组比实际执行操作更便宜,而不仅仅是使用谓词并过滤时间是否结束。

final long end = System.nanoTime() + TimeUnit.SECONDS.toNanos(30L); 
myDataStructure.stream() 
    .filter(e -> System.nanoTime() <= end) 
    .forEach(e -> 
    { 
     ... 
    }); 

问题是如果您需要知道哪些元素已被处理或没有。有了上述内容,你必须检查是否发生了某个特定元素的副作用。

+0

哦,我不认为使用'filter'那样的想法 – Hatefiend

+11

不错的想法,但是如果流很大,为每个元素评估System.currentTimeMillis()<= start + 30000L'可能是浪费的30秒过去了。 – Eran

+1

@Eran是的,看起来像一个带'get'的自定义池会更合适 – Eugene

15

由于forEach没有break,我想你可以创建这个自定义异常break循环:

myDataStructure.stream() 
    .forEach(e -> 
    { 
     if (System.currentTimeMillis() <= start + 30000) { 
      throw new MyTimeOutException() 
     } 
    }); 

,您可以看看这个异常这个。

+0

不幸的是,异常对于这个应用程序来说开销太大了。 – Hatefiend

+14

@Hatefiend异常仅在30秒后抛出一次。我认为创建和抛出异常的开销可以忽略不计。 – SpaceTrucker

+0

这是做这件事的最有效的方法。抛出一次抛出异常比检查大数据集中每个不需要的元素的条件更有效。 – nafas

5

您可以使用这一事实.allMatch()是一个短路操作终止流:

final long start = System.currentTimeMillis(); 
myDataStructure.stream() 
    .allMatch(e -> 
    { 
     // your task here 
     return System.currentTimeMillis() <= start + 30000; 
    }); 
+0

这将工作,但'allMatch'终止流。我目前使用'anyMatch',两者不能很好地混合在一起。我也不能用'anyMatch'短路。 – Hatefiend

+1

@Hatefiend这是问题中所述问题的解决方案。如果您有限制/边界条件/您现在描述的其他要求,最好在问题/问题陈述中提及 –

17

我将创建一个自定义的游泳池,是这样的:

ForkJoinPool forkJoinPool = new ForkJoinPool(1); 
    try { 
     forkJoinPool.submit(() -> 
     IntStream.range(1, 1_000_000).filter(x -> x > 2).boxed().collect(Collectors.toList())) 
       .get(30, TimeUnit.MILLISECONDS); 
    } catch (TimeoutException e) { 
     // job not done in your interval 
    } 
+0

据我所知,这应该在超时时间早期返回,但fork连接池停止处理不消耗比必要的更多的CPU资源?在我看来,它会继续在后台处理流。您的代码中的OP还在哪里? – SpaceTrucker

+2

@SpaceTrucker在达到超时时可以显式调用“shutDown”或“shutDownNow”。关于代码 - 这只是一个例子... – Eugene

+0

我认为你的解决方案是最好的解决方案。 +1 –

3

至于什么根据OP的评论,在Java 8中错过了JavaBean(将在Java 9中添加)。没有任何理由尝试通过异常或其他代码来实现逻辑,因为代码看起来非常丑陋,并且非正则代码甚至仅仅用于实践。我认为使用第三方库是一个好得多的解决方案,例如StreamEx

StreamEx(source).takeWhile(() -> System.currentTimeMillis() <= start + 30000) 
       .forEach(e -> { ... });