カテゴリ: Java 更新日: 2026/03/01

Java Setをループ処理する方法まとめ|for・forEach・Streamを徹底解説

Java Setをループ処理する方法まとめ|for・forEach・Stream
Java Setをループ処理する方法まとめ|for・forEach・Stream

先生と生徒の会話形式で理解しよう

生徒

「JavaのSetを使っているのですが、リストみたいに中身を順番に取り出して表示する方法がわからなくて困っています。」

先生

「SetはListと違ってインデックス(添え字)がないので、通常のfor文で get(i) のように取得することはできません。でも、拡張for文やStream APIを使えば簡単にループ処理ができるんですよ。」

生徒

「そうなんですね!具体的なコードの書き方や、それぞれの使い分けを教えてもらえますか?」

先生

「もちろんです。初心者の方でもすぐに使えるように、基本から応用まで順番に解説していきますね!」

1. JavaのSetインターフェースとループ処理の基本

1. JavaのSetインターフェースとループ処理の基本
1. JavaのSetインターフェースとループ処理の基本

Javaのプログラミングにおいて、Setは重複した要素を許さない特別なコレクションです。数学の集合と同じ概念で、同じ値を二つ入れることはできません。代表的なクラスにHashSetTreeSetLinkedHashSetがあります。

初心者の方が一番最初に躓くポイントは、「Setには順番という概念がない(またはListとは異なる)」という点です。そのため、for (int i = 0; i < set.size(); i++) というお馴染みの書き方が使えません。Setの中身を一つずつ確認したり、加工したりするには、専用のループ処理の方法を学ぶ必要があります。主な手法としては「拡張for文」「Iterator」「forEachメソッド」「Stream API」の四つが挙げられます。これらをマスターすることで、データの集計やフィルタリングが自由自在に行えるようになります。

2. 最もシンプルな拡張for文によるループ処理

2. 最もシンプルな拡張for文によるループ処理
2. 最もシンプルな拡張for文によるループ処理

JavaでSetの要素を一つずつ取り出す最も標準的で読みやすい方法が「拡張for文(enhanced for loop)」です。この方法は、配列やListでも共通して使えるため、Java学習者が最初に覚えるべき必須テクニックです。

拡張for文のメリットは、記述が非常にシンプルであることと、内部的な反復処理をJavaが自動で行ってくれるため、記述ミスが減ることです。Setから取り出される要素の型を指定するだけで、全要素を順番に処理できます。ただし、ループの途中で要素を削除しようとすると例外が発生する場合があるため、その点は注意が必要です。


import java.util.HashSet;
import java.util.Set;

public class SetLoopExample {
    public static void main(String[] args) {
        Set<String> fruits = new HashSet<>();
        fruits.add("りんご");
        fruits.add("バナナ");
        fruits.add("オレンジ");

        // 拡張for文でのループ処理
        for (String fruit : fruits) {
            System.out.println("フルーツ名: " + fruit);
        }
    }
}

フルーツ名: バナナ
フルーツ名: オレンジ
フルーツ名: りんご

3. 柔軟な操作が可能なIterator(イテレータ)の使い方

3. 柔軟な操作が可能なIterator(イテレータ)の使い方
3. 柔軟な操作が可能なIterator(イテレータ)の使い方

昔ながらの方法ではありますが、現在でも重要なのがIterator(イテレータ)を使用したループです。拡張for文の裏側ではこのイテレータが動いています。イテレータを使う最大のメリットは、「ループの最中に要素を安全に削除できる」という点です。

通常の拡張for文の中で set.remove() を呼び出すと、実行時にエラーが発生してプログラムが止まってしまいます。しかし、イテレータの iterator.remove() メソッドを使えば、特定の条件に合致したデータを取り除きながら処理を続けることができます。大量のデータから不要なものを掃除する際などに非常に役立つ手法です。以下のコードでは、文字数が4文字以上の要素を削除する例を紹介します。


import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class IteratorExample {
    public static void main(String[] args) {
        Set<String> set = new HashSet<>();
        set.add("Java");
        set.add("Python");
        set.add("PHP");
        set.add("Ruby");

        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            String language = it.next();
            if (language.length() >= 5) {
                it.remove(); // 安全に要素を削除
            }
        }
        System.out.println("削除後のSet: " + set);
    }
}

削除後のSet: [Java, PHP, Ruby]

4. ラムダ式でスッキリ書けるforEachメソッド

4. ラムダ式でスッキリ書けるforEachメソッド
4. ラムダ式でスッキリ書けるforEachメソッド

Java 8以降、関数型プログラミングの要素が導入され、forEachメソッドが使えるようになりました。これは、Setオブジェクトに対して直接メソッドを呼び出し、引数に処理内容(ラムダ式)を渡す書き方です。たった一行でループ処理を記述できるため、コードの可読性が飛躍的に向上します。

例えば、コンソールに出力するだけの処理であれば、メソッド参照 System.out::println を使うことで、さらに短く記述することが可能です。モダンなJava開発現場では非常によく使われるスタイルであり、初心者の方も積極的に取り入れていくべき書き方と言えます。変数名の宣言を省略できるため、タイピング量も減り、非常に効率的です。


import java.util.LinkedHashSet;
import java.util.Set;

public class ForEachExample {
    public static void main(String[] args) {
        Set<Integer> numbers = new LinkedHashSet<>();
        numbers.add(10);
        numbers.add(20);
        numbers.add(30);

        // ラムダ式を使ったforEach
        numbers.forEach(num -> System.out.println("数値: " + num));
        
        // メソッド参照を使ったさらに短い書き方
        // numbers.forEach(System.out::println);
    }
}

数値: 10
数値: 20
数値: 30

5. 高度なデータ加工を実現するStream APIの活用

5. 高度なデータ加工を実現するStream APIの活用
5. 高度なデータ加工を実現するStream APIの活用

Setの要素を単にループさせるだけでなく、「特定の条件で絞り込む(フィルタリング)」「値を別の形に変換する(マッピング)」「並べ替える」といった複雑な処理を並行して行いたい場合は、Stream APIが最適です。Streamを使うことで、複数の処理をパイプラインのようにつなげて記述できます。

例えば、数値が入ったSetから偶数だけを取り出して、それを10倍にした結果をリスト化するといった処理も、Stream APIなら数行で完結します。従来のfor文であれば、if文や一時的なリストの作成が必要でコードが肥大化していましたが、Streamを使えば宣言的で分かりやすいプログラムになります。大規模なアプリケーション開発では必須のスキルです。


import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

public class StreamApiExample {
    public static void main(String[] args) {
        Set<Integer> scoreSet = new HashSet<>();
        scoreSet.add(45);
        scoreSet.add(80);
        scoreSet.add(65);
        scoreSet.add(90);

        // 70点以上の要素を抽出して表示する
        scoreSet.stream()
                .filter(score -> score >= 70)
                .sorted() // ソート
                .forEach(score -> System.out.println("合格点: " + score));
    }
}

合格点: 80
合格点: 90

6. Setの種類によるループ時の順番の違いに注意

6. Setの種類によるループ時の順番の違いに注意
6. Setの種類によるループ時の順番の違いに注意

ループ処理自体はどのSetでも同じように書けますが、実は「どの実装クラスを使っているか」によって、取り出される順番が全く異なります。これを知らないと、プログラムが予期せぬ挙動をすることがあります。

HashSetはもっとも高速ですが、要素の順序は完全に保証されません。追加した順番とは無関係に並びます。一方、LinkedHashSetは要素を追加した順番を保持します。最後にTreeSetは、要素を自然順序(数値の大きさやアルファベット順)で自動的にソートして保持します。ループ処理の結果として特定の順番を期待する場合は、目的に合ったSetを選択することが重要です。この特性を理解していれば、ループ処理のデバッグもスムーズに進みます。

7. 状況に応じた最適なループ手法の選び方

7. 状況に応じた最適なループ手法の選び方
7. 状況に応じた最適なループ手法の選び方

これまで紹介した手法の中で、どれを使うべきか迷うこともあるでしょう。基本的には、単純に全件処理したい場合は「拡張for文」か「forEach」を選べば間違いありません。特に最近のJavaでは forEach が好まれる傾向にあります。

しかし、要素の削除が伴う場合は「Iterator」一択です。また、データの加工や抽出が目的であれば「Stream API」を使うのが最もスマートです。プログラミングにおいて大切なのは、最新の技術を使うことだけでなく、後からコードを読む人が意図を理解しやすい書き方を選ぶことです。まずは基本的な拡張for文から練習し、慣れてきたらStream APIを使いこなせるようステップアップしていきましょう。

8. よくあるエラーとデバッグのコツ

8. よくあるエラーとデバッグのコツ
8. よくあるエラーとデバッグのコツ

Setのループ処理で初心者が遭遇しやすいエラーの一つに ConcurrentModificationException があります。これは前述の通り、拡張for文の中で要素を削除しようとした時に発生します。エラーメッセージにこの名前が出てきたら、「イテレータを使わなきゃ!」と思い出してください。

また、Setの中に独自のオブジェクト(自作クラスのインスタンス)を入れる場合は、equalshashCodeメソッドを正しく実装していないと、重複排除が効かなかったり、検索が遅くなったりすることもあります。ループ処理を正しく行うためにも、土台となるSetの仕組みを深く理解することが大切です。エラーが出たときは、スタックトレースをよく読み、どの行でどのような矛盾が起きているかを確認する癖をつけましょう。

カテゴリの一覧へ
新着記事
New1
Quarkus
Quarkus拡張開発をマスター!ビルドプロセスの仕組みと内部構造を徹底解説
New2
Micronaut
Micronautの@Factoryとは?複雑なBean生成を管理するための方法を解説
New3
Quarkus
QuarkusのDIとCDIを完全理解!@Producesでプロデューサーメソッドを使う方法を初心者向けに解説
New4
Java
JavaのStringBufferクラスを徹底解説!スレッド安全な文字列操作の仕組みと使い分け
人気記事
No.1
Java&Spring記事人気No1
Quarkus
Quarkus入門!GitHub ActionsでCI/CDパイプラインを構築して自動ビルドを実現する方法
No.2
Java&Spring記事人気No2
Java
Javaのコンパイルと実行の流れを解説!JVM・JDK・JREの違いも初心者向けに整理
No.3
Java&Spring記事人気No3
Micronaut
Micronautのルーティング設定ガイド!プレフィックス付与とAPIバージョニングの基本
No.4
Java&Spring記事人気No4
Quarkus
QuarkusのCI/CD入門!GitHub Actionsで自動デプロイを実現する方法
No.5
Java&Spring記事人気No5
Micronaut
Micronautのフィルタ徹底解説!HTTPリクエスト共通処理をスマートに追加する方法
No.6
Java&Spring記事人気No6
Java
Java Functionインタフェースの使い方を完全ガイド!map変換と処理チェーンを理解する
No.7
Java&Spring記事人気No7
Java
JavaのString比較を徹底解説!equalsと==の違い、初心者が陥る罠とは?
No.8
Java&Spring記事人気No8
Quarkus
Quarkus拡張開発を徹底解説!仕組みから自作エクステンションの作り方まで