Javaで配列を扱う際、「配列をストリーム(Stream)に変換したい」というケースはよくあります。そんなときによく使われるのが Arrays.stream()
と Stream.of()
の2つのメソッドです。
一見似ているこの2つですが、使い方を誤ると「配列の中身を処理したいのに配列そのものが1つの要素になってしまう」といった落とし穴があります。パフォーマンスやバグの観点からも、両者の違いを正しく理解することは非常に重要です。
この記事では、Arrays.stream()
とStream.of()
の本質的な違いと、どちらを選ぶべきかの判断基準を、具体的なコード例を交えてわかりやすく解説します。
結論:配列の要素を処理したいなら Arrays.stream 一択
まず最初に押さえておくべき結論はシンプルです。
- 配列の中身(各要素)をストリーム処理したい場合は、
Arrays.stream()
を使うのが正解です。 Stream.of()
は配列の種類によって挙動が異なるため、配列処理には不向きです。
この違いを理解していないと、「ループが1回しか回らない」「要素が出力されない」といった意図しない挙動につながる可能性があります。迷ったときは、「何をストリームにしたいのか?」を明確にし、用途に合ったメソッドを選びましょう。
Arrays.streamの使い方と特徴
Arrays.stream()
は 配列からストリームを生成するための専用メソッドです。特に配列の中身を1つずつ処理したい場合に適しています。
int[] nums = {1, 2, 3};
IntStream stream = Arrays.stream(nums);
stream.forEach(System.out::println); // 出力: 1, 2, 3
主な特徴
- 各配列要素をそのままストリーム要素に変換
- プリミティブ型(
int[]
,double[]
など)にも対応 IntStream
、DoubleStream
などのプリミティブ用のストリームが生成されて効率的
メリット
- ボクシング不要なので高速(例:
int
→Integer
の変換が不要) sum()
,average()
,max()
など、集約系の処理が豊富
実際の使用例
// 配列の要素を加工したい場合
int[] scores = {85, 92, 78, 95};
double average = Arrays.stream(scores)
.filter(score -> score >= 80)
.average()
.orElse(0.0);
System.out.println("平均点: " + average); // 出力: 平均点: 90.66666666666667
// 文字列配列の処理
String[] names = {"Alice", "Bob", "Charlie"};
Arrays.stream(names)
.map(String::toUpperCase)
.forEach(System.out::println); // 出力: ALICE, BOB, CHARLIE
Arrays.stream()
は、配列の内容をストリーム処理したいときの基本かつ最適な選択肢です。特にプリミティブ型の配列を扱う場面では、その恩恵を最大限に受けることができます。
Stream.ofの使い方と特徴
Stream.of()
は、複数の値やオブジェクトを手軽にストリーム化できるユーティリティメソッドです。可変長引数(varargs)に対応しており、指定した値をそのままストリームの要素として扱えます。
Stream<String> stream = Stream.of("A", "B", "C");
stream.forEach(System.out::println); // 出力: A, B, C
主な特徴
- 可変長引数(Varargs)を受け取るため、複数の要素を直接指定できる
- 配列を渡した場合の挙動が配列の型によって異なる
配列を渡した場合の挙動
プリミティブ配列の場合
int[] nums = {1, 2, 3};
Stream<int[]> stream = Stream.of(nums);
stream.forEach(System.out::println); // 出力: [I@6073f712(配列のハッシュコード)
System.out.println("要素数: " + Stream.of(nums).count()); // 出力: 要素数: 1
プリミティブ配列は配列全体が1つの要素として扱われます。
参照型配列の場合
String[] strs = {"A", "B", "C"};
Stream<String> stream = Stream.of(strs);
stream.forEach(System.out::println); // 出力: A, B, C
System.out.println("要素数: " + Stream.of(strs).count()); // 出力: 要素数: 3
参照型配列は可変長引数として展開されるため、各要素がストリームに含まれます。
Stream.ofの適切な使用例
// 複数の値を直接指定してストリームを作成
Stream.of("apple", "banana", "cherry")
.filter(fruit -> fruit.startsWith("a"))
.forEach(System.out::println); // 出力: apple
// 複数の配列を結合したい場合
String[] fruits = {"apple", "banana"};
String[] vegetables = {"carrot", "potato"};
Stream.of(fruits, vegetables)
.flatMap(Arrays::stream)
.forEach(System.out::println); // 出力: apple, banana, carrot, potato
使い分け早見表と判断基準
Arrays.stream()
と Stream.of()
はどちらも配列をストリームに変換できますが、目的に応じて使い分ける必要があります。以下の早見表を参考に、最適なメソッドを選びましょう。
やりたいこと | 使うメソッド | 理由・特徴 |
---|---|---|
配列の各要素をストリームに変換したい | Arrays.stream() | 各要素が1つずつストリームに展開される |
複数の値を直接指定してストリームにしたい | Stream.of() | 可変長引数で任意の数の値を渡せる |
プリミティブ型配列を効率よく処理したい | Arrays.stream() | IntStream などが生成され高速 |
複数の配列をまとめて処理したい | Stream.of() + flatMap() | 配列単位での操作が可能 |
ポイントは「配列の中身を処理したいのか、配列そのものを扱いたいのか」です。用途に応じて正しいメソッドを選ぶことで、バグのない意図どおりのコードを書くことができます。
まとめ:ストリーム生成は目的に応じて正しく使い分けよう
JavaのStream APIは非常に強力ですが、その分使い方を誤ると予期しない挙動になることもあります。
配列をストリームに変換したいなら Arrays.stream()
、複数の値を直接指定してストリームを生成したいなら Stream.of()
。この基本ルールを覚えておくだけで、コードの明確さと安全性が大きく向上します。
- プリミティブ配列は必ず
Arrays.stream()
を使用する - 配列の要素を処理したい場合は
Arrays.stream()
が安全 Stream.of()
は値を直接指定する場合に使用し、配列処理は避ける
ストリーム生成時は「何をストリームに変換したいのか?」を明確にし、それに合ったメソッドを選びましょう。そうすることで、バグのない明確でメンテナンス性の高いコードを書くことができます。