問題代碼對比
❌ 錯誤寫法:全局變量 + 返回值雙重累加
public class Directory {
static long size = 0; // 全局變量
public static long getSize(File file) {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
size += getSize(f); // 💥 雙重累加:全局變量累加 + 返回值又累加
}
} else {
size += file.length(); // 全局變量累加
}
return size; // 返回累加後的全局值
}
}
✅ 正確寫法:局部變量累加返回
public class Directory {
public static long getSize(File file) {
long size = 0; // 局部變量,每次調用獨立
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
size += getSize(f); // 只累加返回值
}
}
} else {
size += file.length();
}
return size; // 返回當前文件/目錄大小
}
}
錯誤機制圖解:指數級膨脹
目錄結構:
root/
├── a.txt (100B)
└── sub/
└── b.txt (200B)
正確邏輯 (局部變量):
getSize(root):
size=0
getSize(a.txt) → 返回 100 → size=100
getSize(sub):
size=0
getSize(b.txt) → 返回 200 → size=200
返回 200
size += 200 → size=300
返回 300 ✓
錯誤邏輯 (全局變量 size):
初始 size=0
getSize(root):
getSize(a.txt): size=100, return 100
size += 100 → size=200 (第一次膨脹)
getSize(sub):
getSize(b.txt): size=400, return 400
size += 400 → size=800 (第二次膨脹)
return 800
size += 800 → size=1600 (第三次膨脹)
return 1600 ✗ (真實 300B 變 1600B)
核心原理
| 維度 | 全局變量 | 局部變量 |
| 生命周期 | 整個程序運行期間 | 單次方法調用期間 |
| 遞歸共享 | 所有遞歸層級共享**同一個**變量 | 每層遞歸擁有**獨立**變量 |
| 返回值語義 | 返回「截至目前的總和」 | 返回「當前節點的大小」 |
| 累加邏輯 | 全局累加 + 返回值再累加 = 雙重計算 | 僅通過返回值層層匯總 |
修正後完整代碼
import java.io.File;
import java.util.Scanner;
public class Directory {
public static void main(String[] args) {
System.out.print("請輸入目錄路徑: ");
Scanner scanner = new Scanner(System.in);
String path = scanner.nextLine();
File dir = new File(path);
if (!dir.exists() || !dir.isDirectory()) {
System.out.println("路徑無效或不是目錄");
return;
}
long size = getSize(dir);
System.out.printf("目錄大小: %,d bytes (%.2f MB)%n", size, size / 1024.0 / 1024.0);
}
public static long getSize(File file) {
long size = 0;
if (file.isDirectory()) {
File[] files = file.listFiles();
if (files != null) {
for (File f : files) {
size += getSize(f);
}
}
} else {
size += file.length();
}
return size;
}
}
進階優化:Files.walk (Java 8+)
public static long getSizeModern(Path path) throws IOException {
return Files.walk(path)
.filter(Files::isRegularFile)
.mapToLong(p -> p.toFile().length())
.sum();
}
總結
- 🚫 **遞歸中禁用全局/成員變量累加結果**
- ✅ **每層遞歸用局部變量接收子調用返回值並匯總**
- 💡 **口訣**:遞歸返回「自己管轄的大小」,父層負責「加起來」
- 🔍 **調試技巧**:在遞歸入口/出口打印
size 觀察變化