Java
2021.12.15
JavaのforEachメソッドの使い方!拡張for文との違いや注意点を解説【初心者向け】
2023.11.18


1.JavaのforEachメソッドとは?

Javaで繰り返し処理を行うためには、for文、拡張for文などを使いますよね。今回扱うforEachメソッドも繰り返し処理を行いますが、それらとは異なる部分があります。


比較をしながら見てみましょう。

①拡張for文との違い

ここでは、forEachメソッドと拡張for文の違いを解説します。

// 拡張for文
for(要素の型 任意の変数名 : 配列名、コレクション名){
    // 繰り返し処理
}

拡張for文は配列、コレクションに対してループ処理を行うときに使用します。

//forEachメソッド
コレクション名.forEach(引数 -> 繰り返し処理);

forEachメソッドでは、要素の型の記述が不要になっています。2つの形を比較すると、拡張for文では3行だった処理が、forEachメソッドを使うと1行で記述できました。


このメソッドはコレクション(List、Mapなど)に対してループ処理を行う時に使用します。拡張for文との違いは、配列をそのままで使うことはできず、Listに変換する処理が必要な点です。

②forEachメソッドを使うメリット

forEachメソッドを使うメリットは、コレクションにアクセスする際の処理を自分で記述する必要がないことです。書くコードが多いと、その分間違う可能性も高くなり、デバッグが大変になります。


拡張for文と比べてもforEachメソッドは1行で済むので分かりやすく、プログラムの可読性も高いです。短く記述できるところにはforEachメソッドを使って、コンパクトにまとめることを心がけてみましょう。

③forEachメソッドを使うと便利なシーン

forEachメソッドは、ListやMapなどのコレクションにアクセスする際に便利です。「Listの要素を最初から順にすべて取り出す」ような単純な繰り返しは、forEachメソッドで簡単に記述ができます。


配列もListに変換することで同じようにアクセスできます。


2.forEachメソッドの使い方


forEachメソッドはこれまでのfor文、拡張for文などとは使い方が異なります。データ構造によっては使う前に適切な処理が必要な場合もあるので、しっかり学んでおきましょう。

①構文

forEachメソッドは以下のように書けます。

//forEachメソッドの構文
コレクション名.forEach(引数 -> 繰り返し処理);

コレクション名には、繰り返す対象のListやMapの名前を入れます。引数は繰り返し処理に使うためのものです。

②ListでforEachメソッドを使う方法

実際にListでforEachメソッドを使ってみましょう。

import java.util.Arrays; // 必要なインポート
import java.util.List;

public class Java_foreach {
    public static void main(String[] args) {
        List<String> exList = Arrays.asList("a", "b", "c", "d"); // Listを宣言、作成
        exList.forEach(s -> System.out.println(s)); // forEachメソッド
    }
}

出力:

a
b
c
d

Listの要素数などが変わっても、コードを変更する必要はありません。とても簡単な記述で済むことがわかると思います。

③配列でforEachメソッドを使う方法

配列でforEachを使うときにはStream APIを使います。

// インポートなど省略
String[] exArray = { "a", "b", "c", "d" }; // 配列を宣言、作成
Arrays.stream(exArray).forEach(s -> System.out.println(s)); // forEachメソッド

出力:

a
b
c
d

Arrays.stream(配列名)は、配列からStreamを作成してくれるメソッドです。このメソッドを使うことによって、配列でもforEachが使えるようになります。

④MapでforEachメソッドを使う方法

次はMapでforEachメソッドを使ってみます。

import java.util.HashMap;
import java.util.Map;

public class Java_foreach {
    public static void main(String[] args) {
        Map<String, String> exMap = new HashMap<String, String>() {   //Mapを宣言、作成
            {
                put("a", "A");
                put("b", "B");
                put("c", "C");
                put("d", "D");
            }
        };
        exMap.forEach((key, value) -> System.out.println(key + " : " + value)); // forEachメソッド
    }
}

出力:

a : A
b : B
c : C
d : D

引数がひとつのときは()を省略できますが、Mapは引数が2つあるので()で囲みます。Mapでは引数を2つ指定しないとエラーが発生してしまうので、注意してください。



3.forEachメソッドを使う際の注意点

forEachメソッドは、拡張for文のようなループ構文とは違い、コレクションやStreamに対して使う「メソッド」です。そのため、使えない機能も存在します。注意が必要な部分を確認してみましょう。

①    continue文

forEachメソッドでは、for文などで使っていたcontinue文を使うとエラーが発生してしまいます。


代わりにreturn文を使うことで、continue文と同じ結果を得られます。

// インポートなど省略
List<String> exList = Arrays.asList("a", "b", "c", "d"); // リストを宣言、作成
exList.forEach(s -> { // forEachメソッド
    if (s.equals("c")){ 
        return; // continueの代わりにreturnを使う
    }
    System.out.println(s);
});

出力:

a
b
d

cを飛ばした要素が取得できました。

②    break文

forEachメソッドではbreak文も使用できません。continue文と違い、break文には実用的な代替案が存在しません。

あまり使うべきではありませんが、try-catch文での代替処理を見てみましょう。

List<String> exList = Arrays.asList("a", "b", "c", "d");
try {
    exList.forEach(s -> {
        if (s.equals("c")) {
            throw new RuntimeException();
        }
        System.out.println(s);
    });
} catch (RuntimeException e) {
}

出力:

a
b

ある条件のときに強制的に例外を発生させることによって、確かにbreak文と同じ結果が得られました。しかし行数が多く、何を行っている部分なのかが分かりにくくなっています。これではforEachメソッドを使って記述している意味がありません。


break文を使いたいときは、無理せずfor文やほかのメソッドを使いましょう。

③    インデックス番号の取得

forEachメソッドはカウンタを使わずにループを回すので、インデックス番号はそのままでは取得できません。ここではIntStreamクラスを使う場合、配列をカウンタとしてループの外に用意する場合を見てみます。

IntStreamを使う場合

import java.util.Arrays; // 必要なインポート
import java.util.List;
import java.util.stream.IntStream;

public class Java_foreach {
    public static void main(String[] args) {
        List<String> exList = Arrays.asList("a", "b", "c", "d"); // リストを宣言、作成

        int size = exList.size();    // Listの要素数を取得
        IntStream.range(0, size).forEach(index -> System.out.println(index + " : " + exList.get(index)));
    }
}

出力:

0 : a
1 : b
2 : c
3 : d

インデックスとListの値を同時に取得できました。IntStreamのimportが必要なので気を付けてください。

配列をカウンタとして使う場合

List<String> exList = Arrays.asList("a", "b", "c", "d"); // リストを宣言、作成
        
int[] index = { 0 };
exList.forEach(s -> {    // 処理が複数の場合は{}で囲む
    System.out.println(index[0] + " : " + s);
    index[0]++;
});

出力:

0 : a
1 : b
2 : c
3 : d

配列を使っても同じように取得できました。外で定義した値に代入する場合には、このように変数ではなく配列である必要があります。


4.forEachメソッドを使用するための参考情報


forEachメソッドは Java8に導入されたStreamAPI、ラムダ式などと一緒に知っておくと理解がより深まります。使いこなすためにも、この二つについて知っておきましょう。

①Stream API

Stream APIはJava 8からラムダ式などと一緒に導入され、これによってコレクションに対しての操作がより簡単に行えるようになりました。


使い方は、以下の通りです。

  1. コレクションなどをstreamに変換
  2. 中間操作(map, filterなど)を行う
  3. 終端操作(forEachなど)を行う

forEachはStream APIの終端操作にあたります。


手順からもわかるようにStream APIを使う際にはコレクションをstreamに変換する必要があります。しかし、コレクション(Collection)インタフェースが継承している「Iterableインタフェース」にforEachメソッドがあるため、そのままで使えているのです。

②ラムダ式

ラムダ式もJava 8から導入された記述方式です。ラムダ式を使うと記述が短くなるため可読性が上がったり、メソッドを変数のように扱えたりします。

( 引数 ) -> { 処理 }

が構文です。構文の引数を囲む()と処理を囲む{}は、それぞれ中身がひとつであれば省略できます。


forEachメソッドはラムダ式を使うことで短く記述できています。forEachメソッドを使ったコードと照らし合わせて見てみましょう。

exList.forEach(s -> System.out.println(s));

forEach()の中が、()と{}を省略して書かれたラムダ式になっているのが分かります。


ラムダ式はとても便利な構文です。forEach以外にも様々な記述に使用できることを覚えておきましょう。



この記事をシェア