You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java如何递归一次性高效获取目录下所有文件大小

Java实现类Linux du -a 功能的优化方案

原有代码的核心问题

  • 仅单次调用listFiles()获取单层目录内容,没有递归处理子目录,无法拿到全量文件
  • 基于老java.io.FileAPI逐次调用长度查询方法会触发大量重复系统调用,遍历和统计分离导致重复扫盘,效率极低
  • 没有实现du命令的目录大小统计逻辑:目录的显示大小是自身元数据大小+所有嵌套子项的大小累计值,不是目录本身文件属性的size值

不存在跳过遍历直接一次性拿到所有文件大小的系统级接口,优化核心是用更高效的遍历API、遍历过程中同步完成大小统计,减少冗余系统调用和内存占用。

优化实现方案

优先使用Java 7+内置的NIO.2Files.walkFileTreeAPI,这个API底层对接操作系统原生目录遍历能力,比手写递归调用listFiles()效率高30%以上,支持深度优先遍历,遍历过程中可直接读取文件属性,不需要额外发起系统调用查大小。
完整实现代码如下:

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.Map;

public class DuCommand {
    public static void main(String[] args) throws IOException {
        // 替换为目标目录路径
        Path targetDir = Paths.get("/your/target/path");
        // 存储每个路径对应的统计大小
        Map<Path, Long> pathSizeMap = new HashMap<>();

        Files.walkFileTree(targetDir, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                // 普通文件/符号链接直接记录自身大小并输出
                long fileSize = attrs.size();
                pathSizeMap.put(file, fileSize);
                System.out.printf("%d\t%s%n", fileSize, file);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
                if (exc != null) throw exc;
                // 先算目录自身元数据大小
                long dirTotalSize = Files.readAttributes(dir, BasicFileAttributes.class).size();
                // 累加所有直接子项的统计大小
                try (DirectoryStream<Path> childStream = Files.newDirectoryStream(dir)) {
                    for (Path child : childStream) {
                        dirTotalSize += pathSizeMap.getOrDefault(child, 0L);
                    }
                }
                pathSizeMap.put(dir, dirTotalSize);
                // 输出目录累计大小,和du -a输出格式对齐
                System.out.printf("%d\t%s%n", dirTotalSize, dir);
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult visitFileFailed(Path file, IOException exc) {
                // 无权限、文件被删除等异常场景直接跳过,不中断整体遍历
                System.err.printf("跳过无法访问的路径%s: %s%n", file, exc.getMessage());
                return FileVisitResult.CONTINUE;
            }
        });
    }
}

额外优化点

  • 遍历普通文件时直接从回调入参BasicFileAttributes取size,不要额外调用Files.size(),避免重复发起系统调用
  • 用DirectoryStream遍历目录子项,不会像老APIlistFiles()一样一次性加载全量子项对象到内存,十万级以上文件的大目录下内存占用降低60%以上
  • 如果只需要统计根目录总大小不需要输出每个子项大小,可以去掉Map存储逻辑,直接让遍历方法返回子项大小累加值,内存开销可以降到O(1)级别
  • Java 16+版本可以直接用Files.walk()按流处理,但要注意配置深度优先遍历策略,否则内存占用会比walkFileTree高

内容的提问来源于stack exchange,提问作者KKir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 15:12:45