この記事では、Java Streams を使って「特定の型だけを抽出し、配列としてまとめる」方法について、実際のコードとともに整理します。
背景
例えば、ある配列 attributes の中に複数の型のオブジェクトが混在している場合、
Annotations 型だけを抜き出して、その中の AnnotationsEntity 要素を 1 つの配列にまとめたい、とします。
Object[] attributes = { "string", someAnnotation, 123, anotherAnnotation };
元のコード(OSS例)
Streams.of(attributes)
.filter(Annotations.class::isInstance)
.flatMap(e -> Stream.of(((Annotations) e).toArray(AnnotationsEntity[]::new)));
Streams.of(attributes) → 配列や可変長引数を Stream に変換
filter(Annotations.class::isInstance) → Annotations 型だけ抽出
flatMap(e -> Stream.of(...)) → 配列を Stream に変換(ただし平坦化されない)
注意
このままだと「配列そのものが 1 要素の Stream」になるだけで、配列の中身は平坦化されません。
配列と Stream の関係
配列のイメージ
attributes
├─ attributes[0] → arr1 → [entity1, entity2]
└─ attributes[1] → arr2 → [entityA, entityB]
arr1 や arr2 が Annotations 型で、その中に複数の AnnotationsEntity が格納されている
[entity1, entity2] のカンマは 表示上の表記 で、実際にはメモリ上にカンマ文字は存在しません
Stream.of(array) と Arrays.stream(array)
| メソッド | Stream 内の要素 |
|---|---|
Stream.of(array) |
配列そのものが 1 要素 |
Arrays.stream(array) |
配列の中身が 1 要素ずつ Stream に展開 |
flatMap と組み合わせる場合、中身を展開したいなら Arrays.stream() を使う のが正しい
実装例(配列にまとめる場合)
AnnotationsEntity[] result = Stream.of(attributes)
.filter(Annotations.class::isInstance) // Annotations 型だけ抽出
.flatMap(e -> Arrays.stream(((Annotations) e)
.toArray(AnnotationsEntity[]::new))) // 配列の中身を平坦化
.toArray(AnnotationsEntity[]::new); // 配列に変換
result は AnnotationsEntity 型だけをまとめた配列 になります
他の型(String, Integer など)は無視されます
処理の流れ(図解イメージ)
attributes: [arr1, arr2]
arr1 = [entity1, entity2]
arr2 = [entityA, entityB]
Stream.of(attributes)
↓ filter
Stream([arr1, arr2])
↓ flatMap(Arrays.stream(...))
Stream([entity1, entity2, entityA, entityB])
↓ toArray
AnnotationsEntity[] result = [entity1, entity2, entityA, entityB]
これにより、特定型だけを抽出し、1つの配列にまとめる 処理が完了します
まとめ
Stream.of(array) は配列を 1 要素として Stream にする
Arrays.stream(array) で配列の中身を個別要素として Stream に展開
filter と flatMap を組み合わせて、特定の型の要素だけを抽出して配列にまとめる のが基本パターン
表記上のカンマは見やすくするためのもので、実際の配列や Stream 内には文字として存在しない