JDK 1.5 引入的 增強型 for 循環,是 Java 語法糖中最高頻、最實用的一個。本文從語法定義、字節碼等價轉換、數組與集合遍歷差異、泛型配合、常見陷阱五個維度,徹底講透這個「每天都在用、未必真懂」的語法特性。
for (ElementType element : arrayOrIterable) {
// loop body
}
ElementType:元素類型,數組為組件類型,集合為泛型參數類型element:局部變量,每次迭代賦值為當前元素arrayOrIterable:數組或實現 Iterable 接口的對象// 源碼
int[] nums = {1,2,3,4,5,6};
for (int n : nums) {
System.out.print(n);
}
// javac 編譯後等價邏輯 (偽代碼)
int[] nums = {1,2,3,4,5,6};
for (int i = 0; i < nums.length; i++) {
int n = nums[i]; // 關鍵:先賦值給局部變量
System.out.print(n);
}
結論:數組增強 for 零額外開銷,純語法糖,編譯器直接展開為索引循環。
// 源碼
List list = new ArrayList<>();
for (Integer n : list) {
System.out.print(n);
}
// 等價邏輯
List list = new ArrayList<>();
for (Iterator it = list.iterator(); it.hasNext(); ) {
Integer n = it.next();
System.out.print(n);
}
關鍵點:
iterator() 調用與 hasNext/next 循環n 在循環體外聲明 (JDK 1.5-1.7) 或循環體內 (JDK 8+),影響閉包捕獲行為ConcurrentModificationExceptionList list = new ArrayList<>();
list.add("a"); list.add("b");
// 無泛型時:element 類型為 Object,需強轉
// 有泛型時:element 直接為 String,編譯期類型檢查
for (String s : list) {
System.out.println(s.length()); // 直接調用 String 方法
}
增強 for + 泛型 = 消除 ClassCastException 風險,是現代 Java 代碼的標準寫法。
IntStream.range |
| 無法移除元素 | 直接 list.remove() 拋 CME | 用 Iterator.remove() 或 list.removeIf() (Java 8) |
| 無法替換元素 | 賦值 element = newVal 只改局部變量 | 用索引 list.set(i, newVal) 或流操作 |
list.stream().filter(...).forEach(...) 更聲明式Iterator 或 removeIfint[] 增強 for 無裝箱開銷,List 有裝箱/拆箱int[] 遍歷 | 1.0x (基準) | 1.0x | N/A | 1.3x |
| ArrayList 遍歷 | 1.0x | 0.9x (避免 iterator 分配) | 1.1x | 1.5x |
| LinkedList 遍歷 | 1.0x | 5.0x+ (隨機訪問 O(n²)) | 1.0x | 1.2x |
結論:增強 for 性能與手寫優化代碼持平,LinkedList 僅適合增強 for/Iterator,嚴禁索引訪問。
增強型 for 循環是 Java 為「遍歷」這一高頻操作定制的語法糖:
掌握其編譯原理與適用邊界,才能在代碼審查中自信地說:「這裡用增強 for 是最佳選擇,因為...」