JavaのStream APIで任意のCollectionに変換する

JavaのStream APIでtoListした結果を、独自のListにaddAllしていたソースがあったので、 Collectors#toCollection でできるよ、と教える機会があった。

Collectors#collectingAndThen に比べて知名度が低いようなのでメモ。

collectingAndThen

これは検索するとよく出てくる。

Collectors#collectingAndThen(Collector, Function) で、追加の変換処理を実行できる。

また、Javadocに、サンプルとして不変リストへの変換方法が書いてある。

追加の仕上げ変換が実行されるようにCollectorを適応させます。たとえば次のように、常に不変リストが生成されるようにtoList()コレクタを適応させることができます。

 List<String> people
     = people.stream().collect(collectingAndThen(toList(), Collections::unmodifiableList));

第1引数で渡したCollectorの結果を引数とするメソッドやコンストラクタがあれば、メソッド参照を使ってFunctionとして第2引数に指定できるため、GuavaのImmutable系クラスなども使用可能。

// Guavaの場合
List<String> people = people.stream()
    .collect(collectingAndThen(toList(), ImmutableList::copyOf));

toCollection

こちらが今回の本命。

collectingAndThen の第2引数に指定できるメソッドやコンストラクタがない場合でも、Collectionを実装していれば Collectors.toCollection(Supplier) で、Supplierが返したCollection実装インスタンスにStreamの内容を追加できる。

デフォルトコンストラクタがあれば、第2引数は コレクション実装クラス::new でいい。戻り値は、Supplierが返したインスタンスになる。

// オレオレリスト
class MyList<T> extends ArrayList<T> {
    ...
}

MyList<String> myList = Stream.of("a", "b", "c")
    .collect(toCollection(MyList::new));

collectingAndThen でも、任意のインスタンスを生成して Collection#addAll するFunctionを記述すれば使えるが、こちらのほうがわかりやすいだろう。