導入
Javaエンジニアとして長く現場に立っていると、コードレビューで「なぜ拡張for文やStream APIを使わずに、あえてC言語スタイルのforループ(以下、従来型forループ)を使っているのか?」と問う場面によく遭遇します。従来型forループは、単なる古い書き方ではなく、特定のユースケースにおいて欠かせない強力なツールです。本記事では、なぜ今あえてこのループを使うべきなのか、その価値と適切な使い分けについて解説します。
基礎知識
従来型forループは、`for (初期化式; 条件式; 更新式) { … }` という構造を持ちます。これに対し、Java 5で導入された拡張for文(for-each)は、`for (要素型 変数 : 配列またはIterable)` と記述し、内部的にIteratorを利用します。従来型forループの最大の特徴は、ループカウンタ(インデックス)を直接操作できる点にあります。この「インデックスの制御権」が、現代のJava開発における使い分けの鍵となります。
実装/解決策
従来型forループを選択すべき主なシーンは以下の3点です。
1. インデックス値そのものが必要な場合(配列の偶数番目のみ処理するなど)。
2. 複数の配列やリストを同時に走査する必要がある場合。
3. ループの途中でインデックスをスキップ、あるいは逆に巻き戻すなど、特殊な制御が必要な場合。
これら以外の「単に全要素を舐めるだけ」の処理には、可読性が高く安全な拡張for文やStream APIを優先すべきです。
サンプルプログラム
以下のコードは、インデックスを活用した実務的な処理例です。
public class LoopExample {
public static void main(String[] args) {
String[] items = {"A", "B", "C", "D", "E"};
// 1. インデックスを使った特定条件の処理(例:偶数番目のみ出力)
// 拡張for文ではインデックスの管理が難しいため、従来型が適しています
for (int i = 0; i < items.length; i++) {
if (i % 2 == 0) {
System.out.println("インデックス " + i + " の要素: " + items[i]);
}
}
// 2. 複数の配列を同時に走査する場合
// 従来型であれば、一つのインデックスで両方の配列にアクセス可能です
int[] prices = {100, 200, 300, 400, 500};
for (int i = 0; i < items.length; i++) {
System.out.println(items[i] + "の価格は " + prices[i] + " 円です");
}
}
}
応用・注意点
現場で最も注意すべきは「オフバイワンエラー(添字の1ズレによるバグ)」です。特に `i <= items.length` と書いてしまい、`ArrayIndexOutOfBoundsException` を引き起こすケースは後を絶ちません。 また、リスト(List)に対して従来型forループで `get(i)` を繰り返すと、`LinkedList` のようなデータ構造の場合、要素へのアクセスがO(n)となり、ループ全体でO(n^2)の計算量になってしまうという重大なパフォーマンス劣化が起こります。Listを走査する場合は、たとえ従来型を使うとしても `size()` で回すのではなく、イテレータを使用するか、`ArrayList` であることが保証されている場合に限定すべきです。 「とりあえず何でもStream」や「とりあえず何でも拡張for」とするのではなく、「インデックス操作が必要か否か」を判断基準にすることで、より意図が明確なコードを目指しましょう。

コメント