2017-08-01 92 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 -> { ... });