Javaのラムダ式で配列を扱う!Arrays.streamの基本と注意点を初心者向けに解説
生徒
「先生、配列をJavaのラムダ式で扱うにはどうしたらいいんですか?」
先生
「いい質問ですね。Javaの配列はArrays.streamを使うことで、ラムダ式やStream APIと一緒に処理できますよ。」
生徒
「それってリストのstream()と違うんですか?」
先生
「少しだけ違いますが、考え方は似ています。それでは、Arrays.streamを使った配列の扱い方を詳しく見ていきましょう。」
1. Arrays.streamとは?
Arrays.streamはJavaのjava.util.Arraysクラスが提供するメソッドで、配列をStreamに変換するために使います。リストの場合はlist.stream()でStreamを作れますが、配列はstream()メソッドを直接持っていないため、このArrays.streamを使う必要があります。
例えば、数値の配列をラムダ式でフィルターしたいときにArrays.streamが大活躍します。
int[] numbers = {1, 2, 3, 4, 5, 6};
int sum = Arrays.stream(numbers)
.filter(n -> n % 2 == 0)
.sum();
この例では、偶数だけを抽出して合計を求めています。とてもシンプルに書けますね。
2. 配列とラムダ式の基本的な使い方
ラムダ式と配列を組み合わせると、要素ごとの処理を短く記述できます。例えば文字列の配列をすべて大文字に変換する例を見てみましょう。
String[] words = {"apple", "banana", "cherry"};
List<String> upperList = Arrays.stream(words)
.map(String::toUpperCase)
.collect(Collectors.toList());
このようにmapを使えば、配列の要素を加工・変換する処理もラムダ式で簡潔に実装できます。
3. Arrays.streamとStream.ofの違い
初心者が混乱しやすいポイントとして、Arrays.streamとStream.ofの違いがあります。どちらも配列からStreamを生成できますが、プリミティブ型の場合には注意が必要です。
int[] numbers = {1, 2, 3};
Stream<int[]> stream = Stream.of(numbers); // 配列全体が1要素になる
IntStream intStream = Arrays.stream(numbers); // 正しく分解される
Stream.ofは配列全体を1つの要素として扱うため、要素ごとの処理には向いていません。数値の配列を扱うときはArrays.streamを使いましょう。
4. 配列をソートする処理
配列の要素をソートする場合にもArrays.streamは便利です。たとえば文字列の配列を昇順で並び替えたいときは次のように書けます。
String[] names = {"Suzuki", "Tanaka", "Yamada"};
List<String> sorted = Arrays.stream(names)
.sorted()
.collect(Collectors.toList());
sortedメソッドは自然順で並べ替えを行います。必要に応じて独自の比較ロジックをラムダ式で記述することも可能です。
5. 配列のフィルタ処理と集計
配列の中から特定の条件に合うデータを抽出する処理にもArrays.streamは活用できます。例えば60点以上の得点だけをリストアップする場合は次のように書けます。
int[] scores = {45, 72, 88, 53, 67};
List<Integer> passed = Arrays.stream(scores)
.filter(score -> score >= 60)
.boxed()
.collect(Collectors.toList());
プリミティブ型の配列を扱う場合はboxed()でラップしてからcollectするのがポイントです。
6. nullに注意!Arrays.streamの落とし穴
Arrays.streamを使うときの注意点のひとつは、配列がnullの場合にNullPointerExceptionが発生することです。事前にnullチェックを入れておくと安全です。
String[] data = null;
if (data != null) {
Arrays.stream(data)
.forEach(System.out::println);
}
また、空配列であれば例外は出ませんが、nullとは明確に動作が異なるため、取り扱いには気をつけましょう。
7. 配列からの重複排除
配列内の重複を取り除きたい場合も、distinct()を使えばラムダ式で簡単に書けます。
String[] items = {"apple", "banana", "apple", "cherry"};
List<String> unique = Arrays.stream(items)
.distinct()
.collect(Collectors.toList());
配列からユニークな要素だけを取り出す処理は、データ処理や帳票出力などの実務でもよく使われます。
まとめ
Arrays.streamとラムダ式で配列操作を理解する
ここまで、Javaのラムダ式とArrays.streamを使った配列の扱い方について、基礎から注意点まで順を追って確認してきました。従来のfor文を使った配列処理と比べると、Arrays.streamとStream APIを組み合わせることで、配列の操作をとても簡潔かつ直感的に記述できることが分かります。配列をStreamに変換するという考え方は、Javaのモダンな書き方を理解するうえで欠かせない重要なポイントです。
特にArrays.streamは、配列をそのままStreamとして扱えるため、filterやmap、sorted、distinctなどの中間操作を自然に組み合わせることができます。数値配列の集計処理や、文字列配列の変換、条件による抽出など、よくある処理を短いコードで表現できる点は、可読性と保守性の両面で大きなメリットといえるでしょう。Javaで配列処理を行う場面では、Arrays.streamを使う選択肢を常に意識しておくことが大切です。
Stream.ofとの違いとプリミティブ型の注意点
記事の中でも触れたように、Arrays.streamとStream.ofは似ているようで挙動が異なります。特にint配列やdouble配列といったプリミティブ型の配列では、この違いを理解していないと、意図しない処理結果になってしまいます。Stream.ofを使うと配列全体が一つの要素として扱われてしまい、要素ごとの処理ができません。そのため、配列を分解して要素単位で処理したい場合は、Arrays.streamを使う必要があります。
また、プリミティブ型のStreamでは、collectを使ってListに変換する際にboxedが必要になる点も重要です。この仕様を知らないと、なぜListに変換できないのか分からず戸惑ってしまうことがあります。こうした細かな違いを理解することで、Arrays.streamをより安全かつ正確に使いこなせるようになります。
nullチェックと実務での使いどころ
Arrays.streamを使う際に見落としがちなのが、配列がnullの場合の挙動です。空配列であれば問題なくStreamは生成されますが、nullの場合はNullPointerExceptionが発生します。そのため、実務で外部データやユーザー入力を扱う場合には、事前にnullチェックを行う習慣が欠かせません。これはラムダ式やStream APIに限らず、Java全体に共通する基本的な考え方でもあります。
配列のソート、重複排除、条件抽出といった処理は、業務アプリケーションやデータ処理の場面で頻繁に登場します。Arrays.streamを使った書き方に慣れておくことで、コード量を減らしつつ、処理内容が伝わりやすいプログラムを書くことができます。結果として、チーム開発でも理解しやすいコードにつながります。
振り返り用サンプルプログラム
ここで、Arrays.streamとラムダ式のポイントを意識した簡単なサンプルプログラムを見てみましょう。条件抽出と並び替えを組み合わせた例です。
String[] fruits = {"orange", "apple", "banana", "apple"};
if (fruits != null) {
List<String> result = Arrays.stream(fruits)
.distinct()
.sorted()
.collect(Collectors.toList());
System.out.println(result);
}
このように、Arrays.streamを使えば、配列に対する一連の処理を流れとして自然に記述できます。どのような順序でデータが加工されているのかも読み取りやすく、Javaの配列操作に対する理解がより深まります。
生徒
「Arrays.streamを使うと、配列の処理がこんなにスッキリ書けるんですね。for文よりも流れが分かりやすい気がします」
先生
「そうですね。ラムダ式とStream APIは、配列やコレクションの処理を読みやすくするための仕組みです。Arrays.streamはその入口だと考えると分かりやすいですよ」
生徒
「Stream.ofとの違いや、boxedが必要な理由も、だいぶ理解できました」
先生
「それは良いですね。プリミティブ型とオブジェクト型の違いを意識できるようになると、Javaの理解が一段階上がります」
生徒
「これからは配列を扱うときに、まずArrays.streamを使えるか考えてみます」
先生
「その意識が大切です。用途に応じてfor文と使い分けながら、ラムダ式とArrays.streamを上手に活用していきましょう」