第2章 ストリーム API の使い方 : 問題 11 : ストリームの処理結果を ArrayList に収集
問題 11
単一のArrayList
がストリームと同じ大きさで生成されている場合、複数のArrayList
をマージしないで、そのArrayList
に結果を並行して収集できるはずである。
なぜなら、互いに異なる位置に並行して行うset
操作なら、スレッドセーフになるからである。
この収集をどうやって達成することができるか?
解答
この問題は、いまいち出題者の意図が分からない。
そもそも、ストリームのサイズを取得するのは終端操作なので「ストリームと同じ大きさでArrayList
を生成」は非現実的である。
それでもトライしてみましょうか…
- 二つの要素をまとめる
Pair<S, T>
クラスを導入 - メソッド
Stream<Pair<S, T>> zip(Stream<S> first, Stream<T> second)
を実装
… 練習問題 8 のzip
ではなく、Java 8 で導入するはずだったもの zip
メソッドでStream<Pair<Integer, T>>
をゲット
…Pair
の第一引数には、収集するArrayList
のインデックスをセットStream<Pair<Integer, T>>
をパラレル化- ストリームの中間操作(
filter
やmap
)はPair
の第二引数に適用 - 終端操作は
forEach
でArrayList
に結果をセット
■ Pair<S, T>
クラス
public class Pair<S, T> { public final S first; public final T second; public Pair(S first, T second) { this.first = first; this.second = second; } }
本来はequals
メソッドやhashCode
メソッドを Override するが、ここでは省略
■ zip
メソッド(Iterator
バージョン)
public static <S,T> Stream<Pair<S,T>> _zip(Stream<S> first, Stream<T> second) { Iterator<Pair<S,T>> iterator = new Iterator<Pair<S,T>>() { private Iterator<S> _first = first.iterator(); private Iterator<T> _second = second.iterator(); public Pair<S,T> next() { return new Pair<S,T>(_first.next(), _second.next()); } public boolean hasNext() { return _first.hasNext() && _second.hasNext(); } } Spliterator<Pair<S,T>> spliterator = Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED); return StreamSupport.stream(spliterator, false); }
■ 処理結果を収集
List<String> words = ~; // ストリームの元ネタ int size = words.size(); // 処理結果を収集する可変長配列 ArrayList<String> list = new ArrayList<String>(Arrays.asList(new String[size])); zip(IntStream.iterate(0, n -> n + 1).mapToObj(Integer::new), words.stream()) .parallel() .map(UnaryOperator.identity()) // Pair の第二引数を変換 .forEach(pair -> list.set(pair.first, pair.second));