1. 介紹
在學習 Java 時,你會常常碰到「增強型 for 迴圈」與「for‑each 迴圈」等關鍵字。如果你習慣使用傳統的 for 迴圈,可能會好奇「兩者有什麼差別?」或「什麼時候該使用它?」
本文將詳細說明 Java 的增強型 for 迴圈(for‑each 迴圈),從基礎概念到實務應用、與傳統 for 迴圈的差異、常見錯誤、重要注意事項以及在真實開發中常用的 FAQ。
增強型 for 迴圈是一項便利的功能,讓你在處理陣列或集合等多筆資料時,能寫出簡潔且易讀的程式碼。本指南旨在回答「為什麼」與「怎麼做」的問題,適合從 Java 初學者到在實務專案中使用 Java 的中階開發者。
閱讀本文後,你將系統性地了解如何使用增強型 for 迴圈、何時該在傳統 for 迴圈與增強型 for 迴圈之間做選擇,並掌握進階的使用模式。如果你想讓 Java 迴圈處理更有效率或提升可讀性,這篇指南將特別有幫助。
2. 增強型 for 迴圈(for‑each 迴圈)概觀
增強型 for 迴圈(for‑each 迴圈)是 Java 5(JDK 1.5)引入的迴圈語法。英文中稱為 “enhanced for statement” 或 “for‑each loop”。它最大的優點是相較於傳統 for 迴圈,能寫出更簡潔的程式碼。
此語法主要用於想要依序處理陣列或集合(例如 List、Set)中的每一個元素時。使用傳統 for 迴圈時,需要自行準備索引變數並手動管理元素數量與邊界條件,而增強型 for 迴圈則省掉了這些工作。
透過增強型 for 迴圈,你可以直觀且安全地執行「取出陣列的每個元素」或「處理 List 中的每筆資料」等操作,同時提升可讀性、降低錯誤發生的機率,這也是它在現代 Java 程式設計中被廣泛採用的原因。
增強型 for 迴圈的主要特性包括:
- Java 5 及之後的版本皆支援
- 可輕鬆存取陣列與集合的所有元素
- 簡化程式碼、提升可讀性
- 有助於防止與邊界相關的錯誤與索引失誤
基於上述原因,建議在需要依序處理多筆元素的情境下,優先考慮使用增強型 for 迴圈。
3. 增強型 for 迴圈的基本語法與使用方式
增強型 for 迴圈(for‑each 迴圈)在想要依序處理陣列或集合的所有元素時,極為便利。其基本語法如下:
for (DataType variable : arrayOrCollection) {
// Processing for each element
}
範例:使用增強型 for 迴圈處理陣列
例如,若要輸出一個 int 陣列的所有元素,可以這樣寫:
int[] numbers = {1, 2, 3, 4, 5};
for (int num : numbers) {
System.out.println(num);
}
在此範例中,numbers 陣列的每個元素依序被指派給 num,System.out.println(num); 會將它印出。相較於傳統的 for 迴圈,省去了索引的處理,使程式碼更簡潔。
範例:處理 List 與其他集合
增強型 for 迴圈同樣適用於 List、Set 等集合。例如,要印出一個 String List 的所有元素:
List<String> names = Arrays.asList("田中", "佐藤", "鈴木");
for (String name : names) {
System.out.println(name);
}
如同前面的例子,names List 的每個元素會依序指派給 name。任何實作了 Iterable 介面 的集合——包括 List、Set 等——都可以使用增強型 for 迴圈來處理。
範例輸出
1
2
3
4
5
或
田中
佐藤
鈴木
增強型 for 迴圈在您想要依序處理所有元素、且不想為複雜的迴圈條件或索引變數煩惱時,是理想的選擇。
4. 與傳統 for 迴圈的差異
Java 提供兩種迴圈結構:「傳統 for 迴圈(基於索引的 for 迴圈)」與「增強型 for 迴圈(for‑each 迴圈)」。雖然兩者皆用於迭代處理,但各自有不同的優點、缺點與適用情境。
語法差異
傳統 for 迴圈(基於索引)
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
此寫法使用索引 i 來存取陣列或 List 中的每個元素。
因為可以取得索引,所以此方式支援隨機存取、部分迭代、逆序處理等彈性操作。
增強型 for 迴圈(for‑each)
for (DataType element : arrayOrCollection) {
System.out.println(element);
}
此寫法會自動將每個元素指派給變數,並依序處理。
不需要自行管理索引,使程式碼更簡潔。
比較表:增強型 for 迴圈 vs. 傳統 for 迴圈
| Aspect | Enhanced for Loop | Traditional for Loop |
|---|---|---|
| Simplicity of Syntax | ◎ Very simple and intuitive | △ Slightly complex |
| Index Manipulation | × Not possible | ◎ Fully available |
| Element Removal | × Not recommended | △ Possible with proper handling |
| Processing All Elements | ◎ | ◎ |
| Reverse Order Processing | × Not possible | ◎ Easily written |
| Skipping Elements | × Difficult | ◎ Flexible control |
該選哪一種?關鍵決策點
適合使用增強型 for 迴圈的情況:
- 想要處理陣列或集合中的所有元素
- 需要簡潔、易讀的程式碼
- 不需要索引值或逆向處理
適合使用傳統 for 迴圈的情況:
- 需要索引值(例如存取特定位置、逆序迴圈、或跳過某些元素)
- 需要在迭代過程中加入或移除元素,或使用 iterator 進行較複雜的操作
了解兩者的差異並依情境選擇合適的迴圈,對於撰寫高效且安全的 Java 程式碼至關重要。
5. 增強型 for 迴圈的實務應用
增強型 for 迴圈(for‑each 迴圈)不僅可用於基本的陣列與 List,亦可搭配各種資料型別與真實情境使用。以下列出幾個常見的實作範例。
迭代 Map
Map 以鍵值對的形式儲存資料。使用增強型 for 迴圈時,通常會遍歷 entrySet()。以下範例會印出 Map 中所有的鍵值對:
Map<String, Integer> scores = new HashMap<>();
scores.put("田中", 80);
scores.put("佐藤", 90);
scores.put("鈴木", 75);
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue());
}
透過 entrySet(),一次取得一個 entry(鍵值對)。
迭代二維陣列
增強型 for 迴圈同樣適用於多維陣列。例如,印出 2D 整數陣列的所有元素:
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
for (int[] row : matrix) {
for (int num : row) {
System.out.print(num + " ");
}
System.out.println();
}
外層迴圈取得每一列(1D 陣列),內層迴圈則印出該列中的元素。
迭代物件陣列或 List
增強型 for 迴圈也能用於物件陣列或 List。例如,將 Person 物件存於陣列中,並印出每個人的姓名:
class Person {
String name;
Person(String name) {
this.name = name;
}
}
Person[] people = {
new Person("田中"),
new Person("佐藤"),
new Person("鈴木")
};
for (Person person : people) {
System.out.println(person.name);
}
在 Set 與其他集合上使用增強型 for 迴圈
Set 只包含唯一元素且不保證順序,也可以使用增強型 for 迴圈。例如:
Set<String> fruits = new HashSet<>(Arrays.asList("リンゴ", "バナナ", "オレンジ"));
for (String fruit : fruits) {
System.out.println(fruit);
}
增強型 for 迴圈可用於 Java 所提供的幾乎所有集合與陣列,包括物件集合。
6. 注意事項與不建議使用增強型 for 迴圈的情境
雖然增強型 for 迴圈非常方便,但在某些情況下並非最佳選擇。本節說明重要的注意點與不建議使用增強型 for 迴圈的情境。
需要索引時
在增強型 for 迴圈中,無法取得目前元素的索引(位置)。因此,若要只處理偶數索引的元素或存取特定索引範圍時,傳統的 for 迴圈較為合適。
範例:在傳統 for 迴圈中使用索引
int[] numbers = {1, 2, 3, 4, 5};
for (int i = 0; i < numbers.length; i++) {
if (i % 2 == 0) {
System.out.println(numbers[i]);
}
}

新增或移除元素時
如果在使用增強型 for 迴圈時嘗試向集合中新增或移除元素,Java 可能會拋出 ConcurrentModificationException。在迭代過程中修改集合大小時,建議使用 Iterator。
範例:使用 Iterator 移除元素
List<String> names = new ArrayList<>(Arrays.asList("田中", "佐藤", "鈴木"));
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
if (name.equals("佐藤")) {
iterator.remove();
}
}
在增強型 for 迴圈內執行相同操作會導致錯誤,需特別小心。
處理 null 或空的陣列/集合
對 null 陣列或集合使用增強型 for 迴圈會拋出 NullPointerException。在處理前務必先做 null 檢查。
範例:實作 null 檢查
int[] numbers = null;
if (numbers != null) {
for (int num : numbers) {
System.out.println(num);
}
}
逆向處理或條件跳過
增強型 for 迴圈總是依序從第一個元素處理到最後一個元素。如果需要逆向處理或根據條件跳過某些元素,傳統的 for 迴圈較為適合。
總結來說,增強型 for 迴圈在需要順序處理所有元素時最為強大。但若需基於索引的操作、元素修改或複雜的迴圈控制,應改用傳統 for 迴圈或 Iterator 等其他迴圈結構。
7. 常見錯誤與除錯方法
雖然增強型 for 迴圈(for‑each 迴圈)簡單且安全,但不當使用仍可能導致意外錯誤或 bug。本節說明實務開發中常見的錯誤類型以及解決方式。
NullPointerException
當使用增強型 for 迴圈處理 null 陣列或 null 集合 時,會拋出 NullPointerException。這通常是因為資料結構尚未初始化所致。
範例:會產生錯誤的程式碼
List<String> names = null;
for (String name : names) { // ← NullPointerException
System.out.println(name);
}
解決方案:加入 null 檢查
List<String> names = null;
if (names != null) {
for (String name : names) {
System.out.println(name);
}
}
或者,也可以在使用前先初始化集合,這樣較為安全。
移除元素時的 ConcurrentModificationException
如果在增強型 for 迴圈中嘗試移除或新增元素,Java 會拋出 ConcurrentModificationException。這是 Java 內部安全機制的結果,也是初學者常犯的錯誤。
範例:會產生錯誤的程式碼
List<String> names = new ArrayList<>(Arrays.asList("田中", "佐藤", "鈴木"));
for (String name : names) {
if (name.equals("佐藤")) {
names.remove(name); // ← ConcurrentModificationException
}
}
解決方案:使用 Iterator
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
if (name.equals("佐藤")) {
iterator.remove(); // Safe removal
}
}
更改陣列或集合的大小
在增強型 for 迴圈中,Java 會在迴圈開始前確定元素的數量。 因此,如果在迴圈執行期間嘗試改變資料結構的大小(例如加入或移除元素),迴圈可能會出現意外行為。 特別是陣列的大小是固定的,迭代時無法更改其長度。
型別不匹配錯誤
在增強型 for 迴圈中,語法要求 DataType variable : arrayOrCollection。
如果宣告的資料型別與陣列或集合實際的元素型別不符,編譯時會產生錯誤。 範例:型別不匹配錯誤
List<Integer> numbers = Arrays.asList(1, 2, 3);
// for (String num : numbers) { ... } // ← Compile error
for (int num : numbers) { // or Integer num : numbers
System.out.println(num);
}
雖然增強型 for 迴圈是一個強大的工具,但留意這些常見的陷阱能幫助你撰寫更安全、無錯誤的程式。
8. 小結
增強型 for 迴圈(for-each 迴圈)提供了一種簡潔且安全的語法,讓 Java 在處理陣列與集合時更加方便。 相較於傳統的 for 迴圈,它能產生更短、更易讀的程式碼,這也是它在許多情境下被廣泛使用的原因。 當你想要依序處理陣列或集合的所有元素時,增強型 for 迴圈特別有效。 由於語法簡單,你可以寫出更乾淨的程式碼,而不必擔心迴圈範圍或索引的處理。 然而,當需要使用索引、修改元素、進行逆向處理,或跳過特定元素時,使用傳統的 for 迴圈或 Iterator 會更合適。 了解增強型 for 迴圈的機制與限制,能讓你在不同情況下選擇最適合的迴圈方式。 在本文中,我們討論了增強型 for 迴圈的基礎與進階用法、與傳統 for 迴圈的差異、重要的注意事項,以及常見錯誤的解決方案。 運用這些知識,你就能撰寫更高效且更健全的 Java 應用程式。
9. 常見問題 (FAQ)
Q1. 使用增強型 for 迴圈時,有沒有辦法取得索引?
A1. 沒有。增強型 for 迴圈不提供元素索引的存取。
如果需要索引值,必須改用傳統的 for 迴圈(例如 for (int i = 0; i < array.length; i++))或自行手動管理計數器。
然而,在必須操作索引的情況下,通常不建議使用增強型 for 迴圈。
Q2. 我可以在增強型 for 迴圈內加入或移除元素嗎?
A2. 不行。於增強型 for 迴圈中加入或移除元素可能會拋出 ConcurrentModificationException。
如果需要在迭代過程中安全地移除元素,建議使用 Iterator。
Q3. 哪些資料結構可以與增強型 for 迴圈一起使用?
A3. 增強型 for 迴圈適用於陣列以及任何實作了 Iterable 介面的集合(例如 List 與 Set)。
雖然 Map 不能直接以增強型 for 迴圈遍歷,但可以透過 entrySet()、keySet() 或 values() 來處理。
Q4. 使用增強型 for 迴圈遍歷 Map 時,推薦的做法是什麼?
A4. 最常見的做法是:
for (Map.Entry<K, V> entry : map.entrySet()) {
...
}
這允許輕鬆存取鍵和值。
如果您只需要鍵或值,可以迴圈遍歷 keySet() 或 values()。 Q5. 增強型 for 迴圈比傳統 for 迴圈慢嗎? A5. 在大多數日常使用案例中,這兩者之間沒有顯著的效能差異。
雖然極大資料集或高頻率操作可能顯示輕微差異,但在實際開發中,通常優先考慮可讀性和安全性,使增強型 for 迴圈成為常見選擇。 Q6. 增強型 for 迴圈可以巢狀嗎? A6. 可以。您可以用巢狀增強型 for 迴圈來處理多維陣列或巢狀集合。
外部和內部迴圈都可以使用 for-each 格式,這使得對 2D 陣列的操作變得簡單易寫。 Q7. 我該如何在增強型 for 迴圈和 Iterator 之間選擇? A7. 當您需要修改底層集合(例如移除元素)時,請使用 Iterator。
當您只需依序處理所有元素時,請使用增強型 for 迴圈。
根據使用案例,每種方法都有其優勢。
10. 參考連結與相關文章
官方文件與有用外部資源
- Java™ Tutorials (Oracle Official): Enhanced for Statement Oracle 官方 Java 文件中對增強型 for 迴圈的官方解釋,包括語法和範例。
- Java Platform SE 8 API Specification – java.lang.Iterable
Iterable介面的官方文件,可與增強型 for 迴圈一起使用。
推薦進階學習書籍
- “Sukkiri Wakaru Java Nyumon (3rd Edition)” by Kiyotaka Nakayama / Impress
- “Java Language Programming Lessons” by Hiroshi Yuki / SB Creative
我們希望這篇文章能激勵您深入了解 Java 迴圈結構以及集合的正確使用。
