Java Files.walk: เดินต้นไม้ไฟล์แล้วปิด Stream ให้ทัน
Files.walk ให้ Stream ที่ค่อย ๆ เดิน directory tree จึงเขียน filter และ count ได้กระชับ แต่ stream นี้ถือ directory handles ระหว่างการเดิน เอกสาร Files.walk ระบุว่าต้องปิดด้วย try-with-resources ต่างจาก stream ที่สร้างจาก collection ทั่วไปครับ
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
public class Main {
static long countRegularFiles(Path root) throws IOException {
try (Stream<Path> paths = Files.walk(root)) {
return paths.filter(Files::isRegularFile).count();
}
}
public static void main(String[] args) throws Exception {
Path root = Files.createTempDirectory("walk-demo-");
Path nested = Files.createDirectory(root.resolve("nested"));
Path first = Files.writeString(root.resolve("a.txt"), "a");
Path second = Files.writeString(nested.resolve("b.txt"), "b");
try {
assert countRegularFiles(root) == 2;
} finally {
Files.deleteIfExists(second);
Files.deleteIfExists(first);
Files.deleteIfExists(nested);
Files.deleteIfExists(root);
}
}
}
stream เป็น lazy: การเรียก Files.walk(root) ยังไม่ได้อ่านครบทั้งต้นไม้ งานจริงเกิดเมื่อ terminal operation เช่น count, collect หรือ forEach รัน ดังนั้น exception จากการอ่านอาจเกิดกลาง pipeline ได้ และการคืน Stream ออกจาก method จะผลักภาระปิดให้ผู้เรียกโดยมองไม่เห็น ควรทำ terminal operation ภายใน method แล้วคืนผลลัพธ์ที่ไม่ถือ resource
ถ้าต้องควบคุมการข้าม subtree, รับ callback ก่อนและหลัง directory หรือจัดการ error รายไฟล์ Files.walkFileTree จะตรงกว่า ส่วน Files.walk เหมาะกับ query สั้นที่นโยบาย error และ lifecycle อยู่ในบล็อกเดียวกัน
Recap
- ครอบ
Files.walkด้วย try-with-resources ทุกครั้ง - ทำ terminal operation ก่อนออกจาก method
- คาดว่า I/O error อาจเกิดระหว่าง consume stream
- ใช้
walkFileTreeเมื่อต้อง prune หรือควบคุม traversal ละเอียด