2017-04-05 48 views
2

我創建一個列表並使用並行流從其他列表中填充它,意外的是目標列表包含空值。它很少發生,不穩定。有人有同樣的問題嗎?由並行流填充的ArrayList包含空值

下面是一段代碼:

Collection<DestinationObj> DestinationObjList = Lists.newArrayList(); 
SourceObjList.parallelStream().forEach(portalRule -> DestinationObjList.add(new DestinationObj(portalRule))); 
return DestinationObjList; 
+4

你'ArrayList'不是線程安全的。 – Flown

+4

https://docs.oracle.com/javase/tutorial/collections/streams/parallelism.html請參閱頁面末尾;) – 2017-04-05 11:55:38

+2

順便說一句,您應該使用'.collect(...)'而不是執行' .forEach'。我懷疑它可能適用於並行流,即使沒有線程安全列表(因爲收集器將處理同步),但是您必須驗證它。 –

回答

3

你應該收集並聯有點不同的方式:

SourceObjList.parallelStream() 
     .map(DestinationObj::new) 
     .collect(Collectors.toCollection(ArrayList::new)); 

您有問題是ArrayList不線程安全的,因此結果真的沒有定義。 注意,使用並行流不需要線程安全集合 - Lists::newArrayList不需要。

+1

順便說一下,我不會忽略結果的DestinationObjList =賦值,因爲該模式不同於OP的'forEach'方法從根本上說,所以我們不應該將其視爲隱含的。 – Holger

2

使用收集器同步對目標列表的訪問會給您在同步中的性能損失。實際上,您可以在沒有同步的情況下執行相同的操作,因爲您知道源列表的大小,因此可以從一開始就創建所需大小的目標列表。

DestinationObj[] dest = new DestinationObj[sourceObjList.size()]; 
IntStream.range(0, sourceObjList.size()) 
    .parallel() 
    .forEach(i -> dest[i] = new DestinationObj(sourceObjList.get(i))); 
List<DestinationObj> destinationObjList = Arrays.asList(dest); 

編輯:只是把霍爾格的改進這裏爲清楚:

List<DestinationObj> destinationObjList = Arrays.asList(
     sourceObjList 
      .parallelStream() 
      .map(DestinationObj::new) 
      .toArray(DestinationObj[]::new)); 
+3

在這種情況下,數組方法可能比收集器稍微高效一些,但這並不意味着您必須手工完成,'DestinationObj [] dest = sourceObjList.parallelStream().map(DestinationObj :: new) .toArray(DestinationObj [] :: new)'會做同樣的事情,同樣可以利用已知的結果大小,但如果以不再可預測大小的方式更改操作,仍然可以繼續工作。它也允許在沒有臨時變量的情況下執行:'List l = Arrays.asList(sourceObjList.parallelStream().map(DestinationObj :: new).toArray(DestinationObj [] :: new));' – Holger

+0

好點,@霍爾。在源碼流已知大小的情況下,'.toArray'是否保證不添加同步?在使用'collect(Collectors.toCollection(ArrayList :: new))'的時候也應該可以避免同步,就像Eugene的回答一樣。它可以創建一個適當大小的ArrayList,並使用'add(index,element)'從而消除同步的需要。在這種情況下,我的答案是毫無意義的。 –

+2

「收集器」不能通過同步工作。它通過使用['Supplier'](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collector.html#supplier--)創建線程本地容器執行孤立的部分評估。在這個特定的例子中,它意味着每個線程都會將一部分數據收集到自己的ArrayList中,然後部分結果必須通過[組合函數](https://docs.oracle)合併。 com/javase/8/docs/api/java/util/stream/Collector.html#combiner--),這相當於'addAll' ... – Holger