2017-04-05 6 views
2

リストを作成していて、パラレルストリームを使用して他のリストから入力すると、予期せず宛先リストにnullが含まれています。それはめったに起こりません。誰かが同じ問題を抱えていますか?ここで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ページの最後を参照してください) –

+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); 

EDIT:だけ明確にするため、ここでホルガーの改善を置く:

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

いいえポイント、@Holger。 '.toArray'はソースストリームのサイズが既知のこのような場合に同期を追加しないことが保証されていますか?また、Eugeneの答えのように 'collect(Collectors.toCollection(ArrayList :: new)) 'を使用するときに同期を避けることも可能です。適切なサイズのArrayListを作成し、 'add(index、element) 'を使って同期の必要性を取り除くことができます。その場合、私の答えは無意味です。 –

+2

'Collector'は同期によって動作しません。これは、['Supplier'](https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collector.html#supplier--)を使用して、スレッドローカルコンテナを作成します孤立部分評価を行う。この具体例では、各スレッドがデータの一部を自身の「ArrayList」に集めてから、部分的な結果を[combiner 'Function'](https://docs.oracle)を介してマージする必要があります。 'addAll'に相当する... com/javase/8/docs/api/java/util/stream/Collector.html#combiner--)。 – Holger

関連する問題