Java/基礎/反復処理

出典: フリー教科書『ウィキブックス(Wikibooks)』
ナビゲーションに移動 検索に移動

反復 (Iteration) は、同じ処理を繰り返し実行することで、コードの簡潔さと再利用性を向上させるためによく使用されます。Javaには、様々な方法で反復処理を行うことができます。たとえば、for文while文do-while文拡張for文IteratorforEachメソッドなどがあります。

このチュートリアルでは、これらの反復処理方法を解説し、それぞれの使用方法や適切な場面について説明します。また、配列やコレクションに対する反復処理、ループ制御についても解説します。

反復処理は、プログラミングにおいて非常に重要な概念であり、プログラムの処理効率や保守性を向上させるためにも、しっかりと理解しておく必要があります。本チュートリアルを通じて、反復処理について深く理解し、効果的に使用するための知識を身につけましょう。

反復処理の例[編集]

Wikipedia
Wikipedia
ウィキペディアループ文の記事があります。

次のサンプルは、一応Hello worldの日本語版です。 Hello world同様、実用的な意味はありませんが、反復処理の例となっています。

Main.java
class Main {
    public static void main(String[] args) {
        String hello = "世界🌍よこんにちは😀";
        for (int i = 0, len = hello.length(); i < len; i = hello.offsetByCodePoints(i, 1)) {
            System.out.println(Character.toChars(hello.codePointAt(i)));
        }
    }
}

上の例はfor文と呼ばれる構文を使った例です(4行目にキーワード「for」があることに注意してください)。 コンパイルして実行してみましょう。

$ javac Main.java
$ java Main
世
界
🌍
よ
こ
ん
に
ち
は 
😀
“縦書き”でメッセージが表示されます。

このプログラムはwhile文を使って次のように書くこともできます。

class Main {
    public static void main(String[] args) {
        String hello = "世界🌍よこんにちは😀";
        int i = 0, len = hello.length(); 
        while (i < len) {
            System.out.println(Character.toChars(hello.codePointAt(i)));
            i = hello.offsetByCodePoints(i, 1);
        }
    }
}

さらにdo-while文を使って次のような書き方もできます。

class Main {
    public static void main(String[] args) {
        String hello = "世界🌍よこんにちは😀";
        int i = 0, len = hello.length(); 
        do {
            System.out.println(Character.toChars(hello.codePointAt(i)));
            i = hello.offsetByCodePoints(i, 1);
        } while (i < len);
    }
}

またさらに、CharSequence#codePoints()でコードポイントのStreamを取得し、これのforEachメソッドで繰返しを行う次のような書き方も可能です。 これが最もシンプルな書き方です。

class Main {
    public static void main(String[] args) {
        new String("世界🌍よこんにちは😀").codePoints().forEach(cp -> System.out.println(Character.toChars(cp)));
    }
}
もはや構文ですらありませんが、サロゲートペアを含むコードポイントの列を繰返していることを簡素に表現できています。

反復条件によく使われる比較演算子[編集]

各構文の詳細を説明する前に、共通する要素について説明します。

反復には、必ず反復条件を伴います。反復は、反復条件が満たされている間だけ続けられ、条件が満たされなくなると終了する。

反復条件は、比較演算子を使って表現されることがあります。 比較演算子は不等号などの数学記号に似ています。

比較演算子一覧
比較演算子 数学記号 意味
== 左辺と右辺が等しい場合にtrueを返します。
!= 左辺と右辺が等しくない場合にtrueを返します。
< 左辺が右辺よりも小さい場合にtrueを返します。
> 左辺が右辺よりも大きい場合にtrueを返します。
<= 左辺が右辺以下の場合にtrueを返します。
>= 左辺が右辺以上の場合にtrueを返します。
instanceof N/A 左辺のオブジェクトが右辺のクラスのインスタンスである場合にtrueを返します。

==演算子は、プリミティブ型の値またはオブジェクトの参照を比較するために使用されます。!=演算子は、==演算子と同じように使用されますが、2つの値が等しくない場合にtrueを返します。

<><=>=演算子は、プリミティブ型の値の大小関係を比較するために使用されます。

instanceof演算子は、オブジェクトの型を調べるために使用されます。左辺が右辺のクラスのインスタンスである場合、trueを返します。

注意点としては、オブジェクトの比較に==演算子を使用すると、オブジェクトの参照を比較するため、同じ内容のオブジェクトでもfalseを返す場合があります。 オブジェクトの内容を比較する場合は、.equals()メソッドを使用する必要があります。

for文[編集]

Wikipedia
Wikipedia
ウィキペディアfor文の記事があります。

Javaのfor文は、反復処理を行うための制御構造の一つで、指定された回数または条件を満たすまで繰り返します。一般的に、以下の形式で使用されます。

for (初期化式; 継続条件式; 更新式) {
    // 反復処理の本体
}
初期化式
初期化式は、反復処理が始まる前に一度だけ評価される式で、反復処理の前に何らかの変数を初期化するために使用されます。
初期化式では変数を宣言することができ、初期化式で宣言された変数のスコープはfor文が終わるまでです。
継続条件式
継続条件式は、反復処理が継続するかどうかを判定するための式で、trueかfalseを返します。この式がtrueを返し続ける限り、反復処理が繰り返されます。
継続条件式がfalseを返した場合、反復処理は終了します。
更新式
更新式は、反復処理の本体が実行された後に評価される式で、反復変数などの値を更新するために使用されます。

for文の本体は、継続条件式がtrueを返す間、繰り返し実行されます。通常、変数の値を使って何らかの処理を行います。

以下は、1から10までの整数を出力する例です。

for (int i = 1; i <= 10; i++) {
    System.out.println(i);
}
このコードは、1から10までの整数を順番に出力するための反復処理を行います。
for文の初期化式 int i = 1 では、反復処理で使用する変数 i を1で初期化しています。
継続条件式 i <= 10 では、変数 i の値が10以下の間、反復処理を繰り返すことを指定しています。
更新式 i++ では、変数 i を1ずつ増加させます。
反復処理の本体 System.out.println(i) では、変数 i の値を画面に出力しています。
つまり、初期値が1であり、10以下の間、変数 i を1ずつ増加させながら、各ループで変数 i の値を画面に出力することを指定しているため、1から10までの整数が順番に出力されます。

掛け算九九の例[編集]

for文の学習と言えば、典型的な例が掛け算九九を表示するサンプルです。

class Main {
    public static void main(String[] args) {
        for (int y = 1; y <= 9; y++) {
            for (int x = 1; x <= 9; x++) {
                 String buf = String.format("%dx%d=%2d ", x, y, x * y);
                 System.out.print(buf);
            }
            System.out.println();
        }
    }
}
外側のforループは、行ごとに反復処理を行います。 内側のforループは、各行に対して、列ごとに反復処理を行います。
内側のforループの中で、String.formatメソッドを使って、現在の行と列の値に対応する九九の計算結果を文字列として作成し、それをSystem.out.printメソッドで出力しています。
外側のforループの最後には、改行を出力するためにSystem.out.printlnメソッドを呼び出しています。これにより、各行が改行で区切られて表示されるようになります。

拡張for文[編集]

テンプレート:Anchers

Wikipedia
Wikipedia
ウィキペディアforeach文の記事があります。


Javaにおいて、拡張for文(またはfor-each文)は、配列やコレクションなどの要素を繰り返し処理するための簡潔な方法を提供します。 標準的なforループとは異なり、ループカウンタを使用する必要がなく、ループ対象のデータ型に応じてループ変数を明示的に宣言する必要がありません。

構文は以下のようになります。

for (要素の型 変数名 : 配列またはコレクション) {
    // 繰り返し処理
}

以下に、配列やコレクションを用いた拡張for文の例を示します。

// 配列の例
int[] nums = {1, 2, 3, 4, 5};
for (int num : nums) {
    System.out.println(num);
}

// コレクションの例
List<String> fruits = new ArrayList<>();
fruits.add("apple");
fruits.add("banana");
fruits.add("orange");
for (String fruit : fruits) {
    System.out.println(fruit);
}

この例では、配列numsの要素を順に表示するためにint型のループ変数numを使用し、同様に、コレクションfruitsの要素を順に表示するためにString型のループ変数fruitを使用しています。 拡張for文を使用することで、コードがより簡潔で読みやすくなります。

拡張for文
なお、この例では配列を使って説明していますが、拡張for文が使えるのは配列だけではありません。(少し先取りした内容になりますが)コレクションフレームワークや、もっと一般的に言うとIterable<E>を実装したクラスが拡張for文で使えます。具体的には、
for (E var1 : /* Iterable<E>を実装したクラスのインスタンス */) {
  // ここに処理を書く
}

という構文になります。 拡張for文は、そうしたインスタンスにも配列同様の考え方で適用することができます。


while文[編集]

Wikipedia
Wikipedia
ウィキペディアwhile文の記事があります。

Javaにおけるwhile文は、指定した条件式がtrueである間、反復処理を続ける制御構造です。構文は以下の通りです。

while (条件式) {
    // 反復処理の本体
}

条件式がtrueである限り、中括弧内の文が繰り返し実行されます。条件式がfalseになった時点で、while文の実行は終了します。

例えば、1から10までの整数を出力するプログラムは以下のように書けます。

int i = 1;
while (i <= 10) {
    System.out.println(i);
    i++;
}

このプログラムでは、変数iを1で初期化し、iが10以下である限り、iを出力してiを1ずつ増やしています。

ファイルの読み込みの例[編集]

Javaにおいて、whileループを使ってファイルを読み込む方法について説明します。

以下は、例としてファイルの中身を1行ずつ読み込み、コンソールに出力するプログラムです。

import java.io.*;

public class FileReadDemo {
    public static void main(String[] args) {
        try {
            // ファイルの読み込みに必要な準備
            BufferedReader br = new BufferedReader(new FileReader("input.txt"));
            String line;

            // ファイルの1行ずつ読み込み
            while ((line = br.readLine()) != null) {
                // 読み込んだ行をコンソールに出力
                System.out.println(line);
            }

            // ファイルを閉じる
            br.close();
        } catch (IOException e) {
            System.out.println("ファイル読み込みエラー");
        }
    }
}

まず、BufferedReader クラスを使ってファイルを読み込むための準備をします。BufferedReader クラスは、テキストファイルを1行ずつ読み込むためのクラスで、FileReader クラスと組み合わせて使用します。

while ループ内では、BufferedReader オブジェクトの readLine() メソッドを使って、ファイルから1行ずつデータを読み込みます。読み込んだ行が null でない場合は、読み込んだ行をコンソールに出力します。

ファイルの読み込みが終了したら、ファイルを閉じるために close() メソッドを呼び出します。また、エラーが発生した場合は、例外処理を行います。

このように、while ループを使ってファイルを読み込むことができます。ただし、ファイルの読み込みには例外処理が必要であることに注意してください。

参考:(line = in.readLine()) != nullのような書き方について

上記のサンプルでは、while文の反復条件が(line = in.readLine()) != nullと書かれています。 これは代入演算と比較演算を同時に行う書き方で、こうした類の処理によく使われるパターンですが、慣れない間は奇異に見えるかもしれません。

なぜこうした書き方が可能なのかというと、代入演算の式は、その式で代入結果の値を返すからです。 念のため、処理の流れを追ってみましょう。

まず括弧内の処理、つまりline = in.readLine()が実行されます。この式は、変数lineに代入された値を返します。要するに、全体でlineが返されると考えてよいでしょう。

続いて、比較演算が行われます。括弧内をlineに置き換えるとわかりよいでしょう。つまりline != nullという判定が行われることになります。linenullになった場合、行はそれ以上ないというサインなので、ループから抜ける必要があります。

参考:for文で書いた場合

上記のサンプルは、for文を使ってたとえば

import java.io.*;

public class FileReadDemo {
    public static void main(String[] args) {
        try {
            // ファイルの読み込みに必要な準備
            BufferedReader br = new BufferedReader(new FileReader("input.txt"));
            String line;

            // ファイルの1行ずつ読み込み
            for (line = br.readLine(); line != null; line = br.readLine()) {
                // 読み込んだ行をコンソールに出力
                System.out.println(line);
            }

            // ファイルを閉じる
            br.close();
        } catch (IOException e) {
            System.out.println("ファイル読み込みエラー");
        }
    }
}

と書くこともできます。 コードの効率性や可読性、保守性なども考慮して判断する必要があります。

do-while文[編集]

Wikipedia
Wikipedia
ウィキペディアdo-while文の記事があります。

Javaのdo-while文は、while文と同じくループ処理を行うための構文の1つです。ただし、while文と異なり、do-while文はループの最初に1度だけブロック内の処理を実行し、その後に条件式を評価します。条件式がtrueの場合、再びブロック内の処理を実行します。条件式がfalseの場合、ループから抜けます。

do-while文の基本構文は以下のようになります。

do {
  // 処理
} while (条件式);

例えば、1から10までの整数を順番に出力する場合は、次のようにdo-while文を使用することができます。

int i = 1;
do {
    System.out.println(i);
    i++;
} while (i <= 10);

このコードでは、変数iを初期化してから、doブロック内でiを出力し、iを1増やしています。その後、while文でiが10以下である限り、doブロックを繰り返し実行します。結果として、1から10までの整数が順番に出力されます。

do-while文は、ループの最初に必ず1度だけブロック内の処理を実行する必要がある場合に使用することができます。また、条件式がfalseであっても、ブロック内の処理を最低1回実行することが保証されるため、while文と異なり、特定の処理を必ず実行する必要がある場合にも適しています。

Iterator[編集]

JavaのIteratorは、コレクションオブジェクト内の要素を順番に取得するためのインターフェースです。Iteratorは、Javaのコレクションフレームワークの一部であり、java.utilパッケージに含まれています。

Iteratorを使用することで、配列やリストなどのコレクションオブジェクトの要素を順番に取得し、処理を行うことができます。Iteratorは、以下の3つのメソッドを持っています。

  • boolean hasNext(): 次の要素がある場合にtrueを返します。
  • E next(): 次の要素を返します。
  • void remove(): 最後に返された要素をコレクションから削除します。

Iteratorの基本的な使い方は、以下のようになります。

Iterator<E> it = collection.iterator();   // イテレータを取得
while (it.hasNext()) {                    // 次の要素がある場合
    E element = it.next();                // 次の要素を取得
    // 要素に対する処理
}

ここで、collectionは要素を持つコレクションオブジェクトです。まず、iterator()メソッドを使用してイテレータを取得し、hasNext()メソッドで次の要素があるかどうかを確認します。次の要素がある場合、next()メソッドで次の要素を取得し、処理を行います。

また、Iteratorは、要素を削除するためのremove()メソッドを持っています。このメソッドを使用する場合は、必ずnext()メソッドで要素を取得した直後に呼び出す必要があります。例えば、次のように使用することができます。

Iterator<E> it = collection.iterator();
while (it.hasNext()) {
    E element = it.next();
    if (条件) {
        it.remove();  // 条件に合致する要素を削除
    }
}

Iteratorを使用することで、コレクションオブジェクトの要素を順番に取得し、必要な処理を行うことができます。ただし、Iteratorは単方向のイテレーションしかサポートしていないため、要素の逆順の処理が必要な場合は、リストイテレータを使用することが推奨されます。

forEachメソッド[編集]

JavaのforEachメソッドは、Java 8から導入された機能で、配列やコレクションの要素を繰り返し処理するためのメソッドです。forEachメソッドは、以下のように使用します。

arrayOrCollection.forEach(element -> {
    // 要素に対する処理
});

ここで、arrayOrCollectionは、要素を持つ配列またはコレクションオブジェクトです。forEachメソッドは、要素ごとに指定された処理を行うためのラムダ式を引数として受け取ります。

ラムダ式は、->を用いて定義されます。上記の例では、elementという変数が要素を表し、{}内には要素に対する処理が記述されます。この場合、forEachメソッドは、配列またはコレクションの要素を繰り返し、各要素に対して指定された処理を行います。

forEachメソッドは、拡張for文に比べて、コードを簡潔に書くことができます。また、複数の要素に対して同じ処理を行う場合にも適しています。

forEachメソッドは、配列やコレクションの要素を順番に処理するため、要素の追加や削除などの操作を行う場合は、for文またはIteratorを使用する必要があります。また、forEachメソッドは、並列処理にも対応しており、parallelStreamメソッドと組み合わせて使用することで、複数のスレッドを使用した並列処理が可能です。

附録[編集]

チートシート[編集]

以下は、Javaで反復処理を行う際に使用する主要な文法とメソッドのチートシートです。

for文
for (初期化式; 条件式; 更新式) {
  // 処理
}
  • 初期化式: ループの最初に一度だけ実行され、変数の初期化を行う。
  • 条件式: 毎回ループの開始時に評価され、真偽値を返す。
  • 更新式: ループの最後に毎回実行され、変数の更新を行う。
while文
while (条件式) {
  // 処理
}
  • 条件式: 毎回ループの開始時に評価され、真偽値を返す。
do-while文
do {
  // 処理
} while (条件式);
  • 条件式: ループの最後に一度だけ評価され、真偽値を返す。
Enhanced for文
for ( 変数名 : 配列またはコレクション) {
  // 処理
}
  • 型: 配列またはコレクションに含まれる要素の型。
  • 変数名: ループ内で要素にアクセスするための変数名。
Iterator
Iterator<> iterator = コレクション.iterator();
while (iterator.hasNext()) {
   要素 = iterator.next();
  // 処理
}
  • 型: コレクションに含まれる要素の型。varを使うと型推論が可能。
  • iterator(): コレクションからイテレータを取得するメソッド。
  • hasNext(): 次の要素があるかどうかを判定するメソッド。
  • next(): 次の要素を取得するメソッド。
forEachメソッド
配列またはコレクション.forEach(要素 -> {
  // 処理
});
  • forEach(): 配列またはコレクションの要素を1つずつ取り出して、ラムダ式による処理を実行するメソッド。
  • 要素 -> {}: ラムダ式の記法。要素に対する処理を記述する。

用語集[編集]

  • ループ (Loop):同じ処理を繰り返し実行するプログラム構造。Javaではfor文、while文、do-while文などがあり、配列やコレクションに対しても反復処理を行うことができる。
  • 初期化式 (Initialization):for文で使用される、ループの最初に一度だけ実行される式。変数の宣言と初期化を行うことができる。
  • 条件式 (Condition):ループの開始時に評価される式。真偽値を返す。
  • 更新式 (Update):for文やwhile文で使用される、ループの最後に毎回実行される式。変数の更新などを行うことができる。
  • 配列 (Array):同じ型の複数の値を格納できる、固定長のデータ構造。インデックスを指定することで個々の要素にアクセスすることができる。
  • コレクション (Collection):複数のオブジェクトを格納するための可変長のデータ構造。Javaでは、List、Set、Mapなどがある。
  • Enhanced for文 (Enhanced for loop):配列やコレクションに対して反復処理を行うための構文。for (型 変数名 : 配列またはコレクション) { // 処理 }の形式で記述する。
  • Iterator:コレクションに対して反復処理を行うためのインターフェース。hasNext()、next()などのメソッドを提供する。
  • forEachメソッド (forEach):配列やコレクションに対してラムダ式を用いて反復処理を行うためのメソッド。配列またはコレクション.forEach(要素 -> { // 処理 });の形式で記述する。