首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 遞歸遍歷獲取目錄下所有文件名稱完整實現方案

     編輯:Java 相關知識

Java 文件系統操作:遞歸遍歷目錄獲取所有文件名稱最佳實踐

獲取目錄下所有文件(含子目錄)是常見業務需求。本文提供基於 java.io.File 的遞歸實現、Java 7+ Files.walkFileTree 現代化方案、Java 8+ Stream API 一行代碼風格,並對比三種方案的性能與適用場景。

方案一:傳統遞歸實現(JDK 1.2+ 通用)

import java.io.File;

public class FileTraversal {
    public static void main(String[] args) {
        String path = "G:/lxz/20130611";
        listAllFiles(new File(path));
    }

    public static void listAllFiles(File dir) {
        if (!dir.exists()) {
            System.out.println(path + " 不存在");
            return;
        }
        if (!dir.isDirectory()) {
            System.out.println("非目錄路徑");
            return;
        }
        
        File[] files = dir.listFiles();
        if (files == null) return;  // 無權限或 I/O 錯誤
        
        for (File file : files) {
            if (file.isDirectory()) {
                System.out.println(file.getName() + " [目錄]");
                listAllFiles(file);  // 遞歸進入子目錄
            } else {
                System.out.println(file.getName());
            }
        }
    }
}

關鍵點

  • file.isDirectory() 區分目錄/文件
  • file.listFiles() 返回 null 需防禦(無權限、非目錄、I/O 錯誤)
  • 深度優先遍歷,棧深度受限於目錄層級(極深目錄棧溢出風險)

方案二:Files.walkFileTree (Java 7+ NIO.2 標準化)

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;

public class ModernFileTraversal {
    public static void main(String[] args) throws IOException {
        Path start = Paths.get("G:/lxz/20130611");
        Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                System.out.println(file.getFileName());
                return FileVisitResult.CONTINUE;
            }
            
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
                System.out.println(dir.getFileName() + " [目錄]");
                return FileVisitResult.CONTINUE;
            }
            
            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) {
                System.err.println("訪問失敗: " + file + " - " + exc.getMessage());
                return FileVisitResult.CONTINUE;  // 或 TERMINATE
            }
        });
    }
}

優勢

  • 標準化訪問者模式,支援符號鏈接循環檢測、權限異常處理、屬性預讀取
  • 可控制遍歷行為(跳過目錄、終止遍歷、繼續)
  • 自動處理深度遞歸,無棧溢出風險

方案三:Java 8+ Stream API 一行代碼

import java.io.IOException;
import java.nio.file.*;
import java.util.stream.Stream;

public class StreamFileTraversal {
    public static void main(String[] args) throws IOException {
        Path start = Paths.get("G:/lxz/20130611");
        
        // 遞歸遍歷所有文件(含目錄)
        try (Stream<Path> stream = Files.walk(start)) {
            stream.forEach(path -> {
                if (Files.isDirectory(path)) 
                    System.out.println(path.getFileName() + " [目錄]");
                else 
                    System.out.println(path.getFileName());
            });
        }
        
        // 僅文件,過濾目錄
        try (Stream<Path> stream = Files.walk(start)) {
            stream.filter(Files::isRegularFile)
                  .map(Path::getFileName)
                  .forEach(System.out::println);
        }
    }
}

注意

  • Files.walk 返回 Stream<Path> 必須 try-with-resources 關閉(實現 AutoCloseable)
  • 默認跟隨符號鏈接,可用 FileVisitOption.FOLLOW_LINKS 控制
  • 大目錄流式處理記憶體友好

三方案對比總結

維度遞歸 FilewalkFileTreeFiles.walk Stream
JDK 版本1.2+7+8+
代碼複雜度低中極低
異常處理手動訪問者回調Stream 鏈式
符號鏈接安全無保護內建循環檢測可選跟隨
屬性訪問二次 I/O預讀取 BasicFileAttributes需額外調用
並行處理困難困難支援 parallelStream()
記憶體模型全量遞歸棧流式流式

生產環境選型建議

  1. 簡單工具腳本/舊版本維護:傳統遞歸 File.listFiles()
  2. 企業級應用、需健壯異常處理:Files.walkFileTree + SimpleFileVisitor
  3. 現代化代碼庫、函數式風格、並行處理:Files.walk + Stream API
  4. 超大文件系統(百萬級文件):考慮原生 Find 命令或專用索引服務

擴展:常見過濾需求實現

// 按擴展名過濾
stream.filter(p -> p.toString().endsWith(".java"))

// 按文件大小過濾 (>1MB)
stream.filter(p -> {
    try { return Files.size(p) > 1024 * 1024; }
    catch (IOException e) { return false; }
})

// 按修改時間過濾 (最近 7 天)
stream.filter(p -> {
    try { 
        FileTime mtime = Files.getLastModifiedTime(p);
        return mtime.toMillis() > System.currentTimeMillis() - 7L*24*3600*1000;
    } catch (IOException e) { return false; }
})

總結

Java 文件遍歷已從手工遞歸進化為 NIO.2 標準化訪問者模式與 Stream API 函數式風格。根據項目 JDK 版本、業務複雜度、併發需求選擇合適方案,並善用 BasicFileAttributes 預讀取優化 I/O 性能,是文件系統操作的專業素養。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved