JAVA

最終更新日: 2022.08.02 (公開: 2022.08.02)

Javaの「forEachメソッド」は効率化に必須!使い方・応用法を解説

Javaの「forEachメソッド」は効率化に必須!使い方・応用法を解説

Javaの「forEachメソッド」は、繰り返し処理を簡潔に記載できるメソッドです。通常の「forループ」や「拡張for文(for-each文)」などと比べると、ソースコードが大幅に短くなることが魅力です。forEachメソッドは、ListやMapなどのコレクションクラスに搭載されており、「ラムダ式」と組み合わせることでさまざまな便利な使い方ができます。

ラムダ式は構文が特殊なので、「取っつきにくい」と感じている人も多いでしょう。しかし、forEachメソッドを最大限に使いこなすためには、ラムダ式についてもよく知っておくことが大切です。そこで本記事では、JavaのforEachメソッドの機能や使い方に加えて、ラムダ式なども活用した応用テクニックをご紹介するので、ぜひご参考ください。

Javaの「forEachメソッド」とは

Javaの「forEachメソッド」とは

Javaの「forEachメソッド」は、繰り返し処理を行うためのメソッドです。通常の「forループ」や「拡張for文(for-each文)」などの標準構文とは異なり、ListやMapなどのコレクションクラスに搭載されている「メソッド」であることが特徴です。コレクションクラスに加えて、APIを使えば配列に対してもforEachメソッドで繰り返し処理が行えます。

forEachメソッドの大きな特徴は、通常のforループや拡張for文(for-each文)よりも、簡潔なコードを記述できることです。ソースコードが短いほうが、処理の流れを追いやすくバグが少ないプログラムを作りやすくなるため、Javaのプログラミングではソースコードの簡潔さが重要になります。以上の観点から、forEachメソッドは非常に魅力的な機能だといえるでしょう。

forEachメソッドは「拡張for文」とは構文が大きく異なる

forEachメソッドは「拡張for文」とは構文が大きく異なる

「拡張for文(for-each文)」と「forEachメソッド」は、いずれも「forループを簡潔に記載できる機能」という点では同じです。しかし、拡張for文とforEachメソッドは、下記のように構文が大きく異なることがポイントです。

//拡張for文の構文
// 拡張for文の構文

for (データ型 引数 : コレクション名) {

  繰り返し行う処理;

}

 

//forEachメソッドの構文
// forEachメソッドの構文

コレクション名.forEach(引数 -> 繰り返し行う処理);

一見すると、forEachメソッドの構文は不思議な感じがするかもしれませんね。forEachメソッドの中に「->」という記号がありますが、これは「ラムダ式」特有のものです。ラムダ式を使うことで、「処理内容自体を引数として引き渡す」ことができるので、ソースコードが非常に簡潔になります。

ラムダ式の使い方については、後ほど詳しく解説していくので、まずは「拡張for文」と「forEachメソッド」の大まかな違いを、実際のサンプルプログラムで掴んでおきましょう。ちなみに、拡張for文は「for-each文」と呼ばれることもありますが、forEachメソッドと混同することがあるため、基本的には拡張for文と呼ぶことをおすすめします。

//サンプルプログラム
import java.util.ArrayList;

import java.util.List;

public class Main {

  public static void main(String[] args) {

    // int型のリストを生成する

    List<Integer> list = new ArrayList<Integer>();

    // リストに要素を追加する

    list.add(1);

    list.add(2);

    list.add(3);

    

    // 拡張for文でリストの中身を表示する

    for (int i : list) {

      System.out.println(i);

    }

    // forEachメソッドでリストの中身を表示する

    list.forEach(i -> System.out.println(i));

  }

}

 

//実行結果
1

2

3

1

2

3

拡張for文は、「for(型名 変数名 : コレクション名)」という構文で、ループカウンタを使わずにforループを回せます。通常のforループより記述量が少なくなるため、思わぬミスやバグも減らせるはずです。

forEachメソッドは、さらに簡潔な構文になっていますね。こちらは型名も記載する必要がなく、わずか1行でコレクション内のすべての要素にアクセスできます。これこそが、forEachメソッドの大きな魅力なのです。

forEachメソッドを使うメリットと役立つシーン

forEachメソッドを使うメリットと役立つシーン

forEachメソッドを使う大きなメリットは、コレクションの各要素にアクセスする処理を簡潔化できることです。ソースコードが長くなれば、それだけミスが生じるリスクが高くなります。拡張forループも簡潔ではありますが、変数の型を指定しないといけません。

forEachメソッドは基本的に1行で書けるうえに、型名の指定も不要なのでミスが減り、プログラムの可読性やデバッグの容易性も高まります。そのため、「Listの要素を順番に取り出す」などforEachメソッドが使えそうな部分は、できるだけforEachメソッドに置き換えるようにするのがおすすめです。

「ラムダ式」は「関数型インターフェース」を実装する機能

「ラムダ式」は「関数型インターフェース」を実装する機能

そもそも「ラムダ式」とは、「関数型インターフェース」を実装するための機能です。関数型インターフェースは、「抽象メソッドをひとつしか持たないインターフェース」のことで、関数の引数になれるという特徴があります。関数インターフェースを実装するとき、ラムダ式を使わない場合は、下記のように「匿名クラス」を使う必要があります。

//サンプルプログラム
// テスト用の「関数型インターフェース」

interface Test {

  // 抽象メソッドをひとつだけ持つ

  public void method();

}

public class Main {

  public static void main(String[] args) {

    // 関数型インターフェースを「匿名クラス」で実装する

    Test test = new Test() {

      public void method() {

        System.out.println(“Hello World!”);

      }

    };

    

    // 実装した関数型インターフェースを実行する

    test.method();

  }

}

 

//実行結果
Hello World!

ちなみに匿名クラスは、メソッドの中で宣言されて、メソッド内部でしか使われない「その場限り」のクラスのことです。匿名クラスは、宣言とインスタンス化を同時に行い、クラス名を記載しないことが特徴になります。

しかし、匿名クラスを利用した実装は冗長で、処理の流れをつかみにくいことがデメリットです。一方でラムダ式を使えば、下記のようにソースコードを大幅に簡潔化できます。

//サンプルプログラム
// テスト用の「関数型インターフェース」

interface Test {

  // 抽象メソッドをひとつだけ持つ

  public void method();

}

public class Main {

  public static void main(String[] args) {

    // 関数型インターフェースを「ラムダ式」で実装する

    Test test = () -> System.out.println(“Hello World!”);

    // 実装した関数型インターフェースを実行する

    test.method();

  }

}

 

//実行結果
Hello World!

つまり「ラムダ式」というものは、「関数型インターフェース」を実装するための機能で、forEachは引数として関数型インターフェースを受け取るメソッドだということです。引き続き、forEachメソッドの仕組みについて見ていきましょう。

forEachメソッドは引数として「Consumer」オブジェクトを受け取る

JavaのforEachメソッドの引数は、ほとんどの場合は「Consumer」という関数型インターフェースです。Consumerオブジェクトは、ひとつの引数を受け取る「acceptメソッド」を持ち、forEachメソッドの内部でこれが実行されます。つまり、forEachメソッドで使用するラムダ式は、単にこのConsumerを実装しているに過ぎないということなのです。

//サンプルプログラム
import java.util.ArrayList;

import java.util.List;

import java.util.function.Consumer;

public class Main {

  public static void main(String[] args) {

    // int型のリストを生成する

    List<Integer> list = new ArrayList<Integer>();

    // リストに要素を追加する

    list.add(1);

    list.add(2);

    list.add(3);

    // 匿名クラスで「Consumer」を実装し、forEachメソッドでリストの中身を表示する

    list.forEach(new Consumer<Integer>() {

      @Override

      public void accept(Integer i) {

        System.out.println(i);

      }

    });

  }

}

 

//実行結果
1

2

3

冒頭で紹介したサンプルプログラムを、ラムダ式なしで実装すると上記のようになります。このように紐解いてみると、難解な感じがするforEachメソッドやラムダ式も、少しイメージしやすくなるのではないでしょうか。この点を踏まえて、forEachメソッドの基本的な使い方を覚えていきましょう。

また、Mapなど複数の値を保持するコレクションでは、「BiConsumer」など他の関数型インターフェースが使用されますが、基本的には「Consumer」だと覚えておいて問題ありません。

forEachメソッドの基本的な使い方・テクニック5選

forEachメソッドの基本的な使い方・テクニック5選

forEachメソッドの基本的な使い方とテクニックについて、下記5つの観点からサンプルコード付きで解説します。

  • ListクラスでforEachメソッドを使う方法
  • MapクラスでforEachメソッドを使う方法
  • 配列オブジェクトでforEachメソッドを使う方法
  • forEachメソッドでインデックスを取得する方法
  • メソッド参照でラムダ式をさらに省略する方法

ListクラスでforEachメソッドを使う方法

Listクラスは、インスタンスの生成後も自由にサイズを変えることができる「可変長配列」です。配列を発展させたオブジェクトなので、すべての要素に順番にアクセスしたいシーンはよくあります。そんなときは、下記のサンプルコードのようにforEachメソッドを活用しましょう。

//サンプルプログラム
import java.util.ArrayList;

import java.util.List;

public class Main {

  public static void main(String[] args) {

    // String型のリストを生成する

    List<Integer> list = new ArrayList<>();

    // リストに要素を追加する

    list.add(1);

    list.add(2);

    list.add(3);

    list.add(4);

    list.add(5);

    list.add(6);

    list.add(7);

    list.add(8);

    list.add(9);

    // forEachメソッドで各要素を100倍し、それからリストの要素を表示する

    list.forEach(s -> {

      s *= 100;

      System.out.println(s);

    });

  }

}

 

//実行結果
100

200

300

400

500

600

700

800

900

基本的には冒頭でご紹介したサンプルプログラムと同じですが、ラムダ式の内部で、配列の要素に操作を加えている部分が大きく異なります。リストの要素である変数「s」を100倍し、それをprintlnメソッドで表示しています。ちなみに、ラムダ式は内部が1文だけの場合は「{}」は必要ありませんが、2文以上で記述する場合は「{}」で囲まないといけません。

MapクラスでforEachメソッドを使う方法

JavaのMapクラスで全要素を表示する場合、「キー」と「値」に分けてループを回すなど、面倒な処理が必要になります。下記のようにforEachメソッドを使うと、わずか1行のコードで全要素を簡単に記載できます。

//サンプルプログラム
import java.util.HashMap;

import java.util.Map;

public class Main {

  public static void main(String[] args) {

    // ハッシュマップを生成する

    Map<Integer, String> map = new HashMap<>();

    

    map.put(1, “リンゴ”);

    map.put(2, “バナナ”);

    map.put(3, “ブドウ”);

    map.forEach((key, value) -> System.out.println(key + ” : ” + value));

  }

}

 

//実行結果
1 : リンゴ

2 : バナナ

3 : ブドウ

ラムダ式の構文に注目してみましょう。「(key, value) -> System.out.println(key + ” : ” + value)」のように、変数が「key」と「value」の2つになっています。これは、Mapクラスが2つの値をセットで格納するコレクションだからです。したがって、MapクラスのforEachメソッドは、「BiConsumer」という関数型インターフェースを受け取ります。

また、ラムダ式は変数が1つの場合は「()」を省略できますが、2つ以上の場合は「()」で囲む必要があります。そのほかの部分は先ほどまでの例と同じなので、ひとつずつ確認して慣れていきましょう。

配列オブジェクトでforEachメソッドを使う方法

配列オブジェクトそのものには「forEach」メソッドは備わっていないため、そのままではforEachメソッドは使えません。しかし、Javaの「Stream API」を使えば、下記の構文で配列でもforEachメソッドが使えるようになります

Arrays.stream(配列名).forEach(引数 -> 繰り返し行う処理);

まずArraysクラスの「streamメソッド」を呼び出し、その引数に配列を渡します。streamメソッドの戻り値は、IntStreamクラスというリストのようなものです。そのままメソッドチェーンでforEachメソッドを呼び出すと、あとは下記のサンプルコードのようにラムダ式を記載するだけです。

//サンプルプログラム
import java.util.Arrays;

public class Main {

  public static void main(String[] args) {

    // int型の配列を生成する

    int[] nums = new int[] { 1, 2, 3, 4, 5 };

    // Stream APIを活用して、forEachメソッドを使用する

    Arrays.stream(nums).forEach(i -> {

      i *= -1;

      System.out.println(i);

    });

  }

}

 

//実行結果
-1

-2

-3

-4

-5

上記のサンプルプログラムでは、配列をIntStreamに変換し、そのままforEachメソッドを呼び出しています。ラムダ式では各要素の符号を反転させ、その値を表示してみました。

forEachメソッドでインデックスを取得する方法

JavaのforEachメソッドは、通常のforループとは異なり「ループカウンタ」を回さないため、「現在どの要素にアクセスしているか」を示すインデックスを取得できません。たとえば通常のforループであれば、下記のようにループ内でインデックスを使った処理ができますよね。

//サンプルプログラム
public class Main {

  public static void main(String[] args) {

    // int型の配列を生成する

    int[] nums = new int[] { 1, 2, 3, 4, 5 };

    // forループで各要素を表示し、ループカウンタでインデックスも表示する

    for (int i = 0; i < nums.length; i++) {

      System.out.println(i + “番目の要素:” + nums[i]);

    }

  }

}

 

//実行結果
0番目の要素:1

1番目の要素:2

2番目の要素:3

3番目の要素:4

4番目の要素:5

forEachメソッドは非常に便利ではありますが、インデックスを取得したい場合は工夫が必要です。ひとつの方法が、「indexOfメソッド」を活用するというものです。

//サンプルプログラム
import java.util.ArrayList;

import java.util.List;

public class Main {

  public static void main(String[] args) {

    // int型のリストを生成する

    List<Integer> list = new ArrayList<Integer>();

    // リストに要素を追加する

    list.add(1);

    list.add(2);

    list.add(3);

    list.add(4);

    list.add(5);

    list.add(1);

    list.add(3);

    list.add(5);

    // forEachメソッドでリストの中身を表示し、indexOfメソッドでインデックスも表示する

    list.forEach(i -> System.out.println(list.indexOf(i) + “番目の要素:” + i));

  }

}

 

//実行結果
0番目の要素:1

1番目の要素:2

2番目の要素:3

3番目の要素:4

4番目の要素:5

0番目の要素:1

2番目の要素:3

4番目の要素:5

indexOfメソッドは、指定した値もしくは数に該当する要素が、コレクションの何番目にあるかを調べるためのものです。基本的には問題なく動きますが、上記のサンプルプログラムのように、重複する要素がある場合はうまくいきません。なぜなら、indexOfメソッドは「最初の要素」のインデックスを取得するからです。

もうひとつの方法が、ラムダ式の外部に「カウント用変数」を用意すること。これなら、コレクション内に重複要素があっても問題ありません。ただし、ラムダ式内部から外部の変数を使うときはコピーした値が参照されるため、外部の変数を変更することはできません。しかし、配列のような参照型変数を用意すれば、ラムダ式内部からでも値を変更できます。

//サンプルプログラム
import java.util.ArrayList;

import java.util.List;

public class Main {

  public static void main(String[] args) {

    // int型のリストを生成する

    List<Integer> list = new ArrayList<Integer>();

    // リストに要素を追加する

    list.add(1);

    list.add(2);

    list.add(3);

    list.add(4);

    list.add(5);

    // インデックスのカウンタ変数を用意する

    int[] count = {0};

    // forEachメソッドでリストの中身を表示し、外部のカウンタでインデックスも表示する

    list.forEach(i -> System.out.println((count[0]++) + “番目の要素:” + i));

  }

}

 

//実行結果
0番目の要素:1

1番目の要素:2

2番目の要素:3

3番目の要素:4

4番目の要素:5

もし「count」を単なる変数・プリミティブ型にすると、いつまでも「0番目」になってしまいます。これを配列にすることで参照型になり、インデックスを順番にカウントするという狙いどおりの動作になります。

メソッド参照でラムダ式をさらに省略する方法

先ほどのサンプルプログラムでもあった、「list.forEach(i -> System.out.println(i));」という部分に注目してみましょう。ここには引数宣言の「i」と、ラムダ式内部のprintメソッドに引き渡す「i」で、「i」という表記が2回登場しています。

実はこの「i」を省略して「list.forEach(System.out::println);」と書くと、「i」は暗黙の了解で定義されるのです。このような書き方を「メソッド参照」と呼び、さまざまな部分で活用されています。メソッド参照の基本的な構文は下記のとおりです。

クラス名::メソッド名

先ほどの例では、通常の書き方は「System.out.println」とピリオドを使っていましたが、「System.out::println」と「:(コロン)」を使うことがポイントです。ピリオドにすると、コンパイルエラーとなるので注意が必要です。実際に、メソッド参照を使うサンプルプログラムを確認しましょう。

//サンプルプログラム
import java.util.ArrayList;

import java.util.List;

public class Main {

  public static void main(String[] args) {

    // int型のリストを生成する

    List<Integer> list = new ArrayList<Integer>();

    // リストに要素を追加する

    list.add(1);

    list.add(2);

    list.add(3);

    list.add(4);

    list.add(5);

    // forEachメソッドでリストの中身を表示し、「メソッド参照」でさらなる簡潔化!

    list.forEach(System.out::println);

  }

}

 

//実行結果
1

2

3

4

5

前述したように、「list.forEach(System.out::println);」は「list.forEach(i -> System.out.println(i));」と同義です。メソッド参照を使うことで、プログラムがさらに簡潔化するので、ぜひ活用してみましょう。

JavaのforEachメソッドの注意点・デメリット2つ

JavaのforEachメソッドの注意点・デメリット2つ

JavaのforEachメソッドには、下記2つの注意点とデメリットがあります。それぞれの代替手段もご紹介するので、ぜひ参考にしてみてください。

  • 「contine」ではなく「return」を使う必要あり
  • 「break」ではなく「例外処理」を使う必要あり

「contine」ではなく「return」を使う必要あり

forEachメソッドはループ処理を行うための機能ですが、その実態は「関数型インターフェース」のメソッドであって、forループのようなループ構文とは異なります。そのため通常のforループや拡張for文で使う、次の要素へ移行するための「continue」は、forEachメソッド内部では使えません。

forEachメソッドの内部は「メソッド」なので、代わりに「return」を書けば、その処理を終了して次の要素へ移行することができます。実際に、下記のサンプルプログラムで詳しい書き方を確認しておきましょう。

//サンプルプログラム
import java.util.ArrayList;

import java.util.List;

public class Main {

  public static void main(String[] args) {

    // String型のリストを生成する

    List<String> list = new ArrayList<>();

    

    // 3つの要素を追加する

    list.add(“Apple”);

    list.add(“Banana”);

    list.add(“Chocolate”);

    // forEachメソッドでループを回すが、「Banana」だけは読み飛ばす

    list.forEach(s -> {

      if (s.equals(“Banana”)) return; // 「return」が「continue」の役割を果たす

      System.out.println(s);

    });

  }

}

 

//実行結果
Apple

Chocolate

「return」の部分に到達した時点で、その要素に対する処理は終了し、ループは次の要素へ移行します。条件によってラムダ式内部の処理を変えたい場合は、returnを活用してみましょう。

「break」ではなく「例外処理」を使う必要あり

先ほどの「continue」は「return」に置き換えることで代用できましたが、「break」は通常の方法では代替できません。そもそもbreakは、ループ処理自体を終了させるためのもので、次の要素へ移行することもありません。しかし、forEachメソッドの内部はメソッドなので、たとえreturnでその処理を終えたとしても、次の要素へ移行するだけです。

そのため、どうしてもforEachメソッドの処理を打ち切りたい場合は、他のブロックへ瞬間移動できる「例外処理」、つまり「try-catch文」を使わないといけません。ただし、try-catch文はあくまで例外処理のための機能なので、この方法を使うことは推奨しません。また、簡潔に書けるというforEachメソッドの利点を活かせなくなることも問題です。

//サンプルプログラム
import java.util.ArrayList;

import java.util.List;

public class Main {

  public static void main(String[] args) {

    // String型のリストを生成する

    List<String> list = new ArrayList<>();

    

    // 3つの要素を追加する

    list.add(“Apple”);

    list.add(“Banana”);

    list.add(“Chocolate”);

    // forEachメソッドでループを回すが、「Banana」になったら処理を打ち切る

    try {

      list.forEach(s -> {

        if (s.equals(“Banana”)) throw new RuntimeException(“処理を打ち切ります”); // 「throw(例外処理)」が「break」の役割を果たす

        System.out.println(s);

      });

    } catch(RuntimeException e) {

      // 例外が発生したため、forEach内部の処理が打ち切られてここへ移動する

      System.out.println(“プログラムを終了します”);

    }

  }

}

 

//実行結果
Apple

プログラムを終了します

上記のサンプルプログラムは、非常にわかりづらい印象があるのではないでしょうか。本来、try-catch文はこのように使うべきではないので、可能な限り避けるべきです。forEachメソッドではなく、拡張for文を使うようにしたり、そもそも不要な要素はコレクションから取り除いておくなど、代替案を検討する方がいいでしょう。

forEachメソッドを活用してプログラムを効率化しよう!

forEachメソッドを活用してプログラムを効率化しよう!

Javaの「forEachメソッド」は、コレクションに搭載されているループ機能です。拡張for文(for-each文)よりソースコードが簡潔になることが大きなメリット。ラムダ式の知識は必要ですが、ラムダ式はひとつずつ紐解いて見ていけば、それほど複雑怪奇なものではありません。この機会にラムダ式を覚えておくと、Javaの学習が大きく進むでしょう。

forEachメソッドは、コレクションによって使い方が多少異なる部分はありますが、基本的には同じ感覚で使えます。ただし、「continue」や「break」など、通常のforループ・拡張for文で慣れ親しんでいる機能が使えず、代替案が必要なことに注意が必要なこともあります。ループの処理内容によって、forEachメソッドとほかのforループを使い分けるのがおすすめです。

著者紹介

084tjsfdsf983asdlk03

084tjsfdsf983asdlk03

コードカキタイがオススメする記事!

  1. 子供におすすめのプログラミングスクール10選!学習メリットや教室選びのコツも紹介

    2022.01.06

    子供におすすめのプログラミングスクール10選!学習メリットや教室選びのコツも紹介

    #プログラミングスクール

  2. 【完全版】大学生におすすめのプログラミングスクール13選!選ぶコツも詳しく解説

    2022.01.06

    【完全版】大学生におすすめのプログラミングスクール13選!選ぶコツも詳しく解説

    #プログラミングスクール

  3. 【未経験でも転職可】30代におすすめプログラミングスクール8選!

    2022.01.06

    【未経験でも転職可】30代におすすめプログラミングスクール8選!

    #プログラミングスクール

  4. 初心者必見!独学のJava学習方法とおすすめ本、アプリを詳しく解説

    2022.01.06

    初心者必見!独学のJava学習方法とおすすめ本、アプリを詳しく解説

    #JAVA

  5. 忙しい社会人におすすめプログラミングスクール15選!失敗しない選び方も詳しく解説

    2022.01.06

    忙しい社会人におすすめプログラミングスクール15選!失敗しない選び方も詳しく解説

    #プログラミングスクール

  1. 【無料あり】大阪のおすすめプログラミングスクール14選!スクール選びのコツも紹介

    2022.01.06

    【無料あり】大阪のおすすめプログラミングスクール14選!スクール選びのコツも紹介

    #プログラミングスクール

  2. 【目的別】東京のおすすめプログラミングスクール20選!スクール選びのコツも徹底解説

    2022.01.06

    【目的別】東京のおすすめプログラミングスクール20選!スクール選びのコツも徹底解説

    #プログラミングスクール

  3. 【無料あり】福岡のおすすめプログラミングスクール13選!選び方も詳しく解説

    2022.01.06

    【無料あり】福岡のおすすめプログラミングスクール13選!選び方も詳しく解説

    #プログラミングスクール

  4. 【徹底比較】名古屋のおすすめプログラミングスクール13選!選び方も詳しく解説

    2022.01.06

    【徹底比較】名古屋のおすすめプログラミングスクール13選!選び方も詳しく解説

    #プログラミングスクール

  5. 【徹底比較】おすすめのプログラミングスクール18選!失敗しない選び方も徹底解説

    2022.01.06

    【徹底比較】おすすめのプログラミングスクール18選!失敗しない選び方も徹底解説

    #プログラミングスクール