三大循環語法對比
| 特性 | for | while | do-while |
| 語法結構 | for(init; condition; update) { body } | while(condition) { body } | do { body } while(condition); |
| 判斷時機 | 每次迭代前 | 每次迭代前 | 每次迭代後 |
| 最少執行次數 | 0 | 0 | 1 (先執行再判斷) |
| 變量作用域 | init 變量僅循環內可見 | 外部定義,循環後可見 | 外部定義,循環後可見 |
| 適用場景 | 已知次數、計數器、遍歷 | 未知次數、條件驅動 | 菜單交互、至少一次處理 |
基礎語法示例
for 循環
// 經典計數
for (int i = 1; i <= 10; i++) {
System.out.println(i);
}
// 多變量初始化/更新
for (int i = 0, j = 10; i < j; i++, j--) {
System.out.println(i + "+" + j + "=" + (i+j));
}
// 集合遍歷 (索引)
List list = Arrays.asList("A", "B", "C");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
// 無限循環
for (;;) { /* ... */ }
while 循環
// 條件驅動
int sum = 0, i = 1;
while (i <= 100) {
sum += i;
i++;
}
System.out.println(sum); // 5050
// 輸入流讀取
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
String line;
while ((line = br.readLine()) != null) {
if ("exit".equals(line)) break;
System.out.println("Echo: " + line);
}
}
do-while 循環
// 至少執行一次:菜單選擇
Scanner sc = new Scanner(System.in);
int choice;
do {
System.out.println("1. 查詢 2. 新增 3. 退出");
System.out.print("請選擇: ");
choice = sc.nextInt();
switch (choice) {
case 1: query(); break;
case 2: add(); break;
case 3: System.out.println("再見"); break;
default: System.out.println("無效選項");
}
} while (choice != 3);
增強 for 循環 (for-each,Java 5+)
// 集合/數組遍歷 (語法糖,編譯為 Iterator)
for (String item : list) {
System.out.println(item);
}
int[] arr = {1, 2, 3};
for (int num : arr) {
System.out.println(num);
}
// 限制:無索引、不可修改集合結構、不可 break/return 外層方法 (可用標籤)
outer:
for (String s : list) {
if ("stop".equals(s)) break outer; // 跳出標籤循環
}
循環控制語句
| 語句 | 作用 | 適用循環 |
break | 立即退出最近一層循環 | for/while/do-while |
continue | 跳過本次迭代剩餘代碼,進入下一次判斷 | for/while/do-while |
return | 退出當前方法 (含循環) | 所有 |
標籤 label: | 配合 break/continue 控制外層循環 | 巢狀循環 |
// 標籤示例:查找二維數組中首個負數
int[][] matrix = {{1,2}, {3,-4}, {5,6}};
boolean found = false;
search:
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
if (matrix[i][j] < 0) {
System.out.println("Found at [" + i + "][" + j + "]");
found = true;
break search; // 直接跳出雙層
}
}
}
性能與字節碼視角
- for/while/do-while 編譯後字節碼幾乎等價,JIT 編譯器會優化為相同機器碼
- for-each 編譯為 Iterator 模式,非 RandomAccess 集合 (LinkedList) 效率等同手寫 Iterator
- 循環不變量外提:JIT 自動將循環內不變的計算/方法調用外提
- 逃逸分析/標量替換:循環內新建對象若未逃逸,可能優化為棧分配或消除
微優化技巧 (極少數熱點循環才需關注)
// 1. 緩存長度 (ArrayList 獲取 size() 極快,但習慣寫法)
int len = list.size();
for (int i = 0; i < len; i++) { ... }
// 2. 反向循環避免邊界檢查 (JIT 通常自動優化)
for (int i = len - 1; i >= 0; i--) { ... }
// 3. 局部變量引用字段 (避免重複字段讀取)
Object localRef = this.field;
for (int i = 0; i < len; i++) { localRef.method(); }
// 4. 基本類型優於包裝類型 (避免拆箱裝箱)
int sum = 0; // 而非 Integer sum = 0;
for (int i = 0; i < 1000000; i++) sum += i;
函數式替代方案 (Java 8+)
| 命令式循環 | Stream API 替代 | 優勢 |
| 遍歷打印 | list.forEach(System.out::println) | 聲明式、可鏈式 |
| 過濾收集 | list.stream().filter(x>0).collect(toList()) | 管道組合、並行化 |
| 求和/統計 | list.stream().mapToInt(Integer::intValue).sum() | 專用減少操作 |
| 查找首個 | list.stream().filter(...).findFirst() | 短路優化 |
| 分組統計 | list.stream().collect(groupingBy(...)) | 聲明式聚合 |
常見錯誤與避坑
| 錯誤代碼 | 問題 | 修正 |
for(int i=0; i<=list.size(); i++) | 索引越界 (size() 而非 size()-1) | i < list.size() |
| 循環中修改集合結構 | ConcurrentModificationException | 用 Iterator.remove() / removeIf() / 反向索引 |
while(true) { ... } 無 break | 死循環導致 CPU 100% | 確保有退出條件或中斷機制 |
| 浮點數作為循環條件 | 精度誤差導致多執行/少執行 | 用整數計數器或 BigDecimal |
do { } while(false); | 偽裝的一次性代碼塊,可讀性差 | 直接用代碼塊 { } 或提取方法 |
最佳實踐決策指南
已知確切迭代次數/需要計數器?
├─ 是 → for (int i=0; i
總結
Java 提供 for、while、do-while 三大基礎循環 + for-each 語法糖 + Stream API 函數式替代。核心差異在於判斷時機 (前/後)與最少執行次數 (0/1)。性能上 JIT 使基礎循環等效,熱點循環可微優化。選擇原則:已知次數用 for、至少一次用 do-while、條件驅動用 while、複雜管道用 Stream、巢狀跳出用標籤。避開修改集合、浮點判斷、死循環等常見陷阱。