从文件夹中读取 Java 流中的块中的文件名
Posted
技术标签:
【中文标题】从文件夹中读取 Java 流中的块中的文件名【英文标题】:Read filenames in chunks in Java streams from a folder 【发布时间】:2019-04-29 02:27:25 【问题描述】:我有一个目录,里面有数百万个文件。我想将文件名读入 ArrayList。如果我将所有文件名读入一个 ArrayList,它会消耗更多内存。我怀疑如果目录中存在大量文件,Java 可能会抛出堆空间错误。 有没有办法分块/批量读取目录中的文件,每次说5个文件名。
【问题讨论】:
我不确定,因为我没有包含数百万个文件的目录。但也许你可以使用流?例如 Arrays.stream(Paths.get("path/to/file").toFile().list());你可以使用 StringStream 来做你想做的事情与 fileNames 即使我使用像 Arrays.stream(Paths.get("path/to/file").toFile().list()); 这样的代码,这部分Paths.get("path/to/file").toFile().list() 仍然会将所有文件名带入内存。我的问题是使用更少的内存来获取所有文件名。 【参考方案1】:您可以为此使用Path.list,它会返回一个惰性求值的流:
List<String> fileNames = Path.list("path_to_directory")
.map(Path::getFileName)
.collect(Collectors.toList());
文件将被一一处理,这将消耗更少的内存。但是,如果最终列表 fileNames
变得太大,您仍然可能会遇到内存问题。因此流管道中的终端(收集)操作可能会导致一些问题。
但是例如,如果您直接在流上处理文件名(例如使用 forEach 而不收集它们),您可以避免将所有名称加载到内存中。
Path.list("path_to_directory")
.map(Path::getFileName)
.forEach(System.out::println);
// 一个一个地打印文件,而不是同时加载所有文件。
我希望这会有所帮助。
【讨论】:
这也会将所有文件名加载到 fileNames 中。我只需要内存中文件夹中的文件名子集。假设一个文件夹中有 100 个文件,我只需要内存中的 5 个文件。打印完 5 个文件名后,我应该得到接下来的 5 个文件名并打印它们。 目标是通过不在内存中加载所有文件名来使用更少的内存 @Nishanth 如果您不使用 collect,但打印文件名目录,您可以避免将它们全部加载到内存中。请参阅我的答案的第二部分。您到底需要对文件名做什么?打印出来? 不,Anton Balaniuc 先生,我需要从文件夹中流式传输大量文件名。如果我一个一个地获取文件名,它会消耗更多时间。以块的形式获取文件名会更快。例如: f 一个文件夹有100个文件,如果我一个一个打印,它会消耗更多的时间。而是一次打印 10 个文件名将消耗更少的时间。除了使用“forEach”之外,还有什么可能的处理方式,比如“.forEachChunkOfSize(10)”? @Nishanth,流没有这种可能性。本质上,流处理元素一个一个。但您可以并行执行此操作:Path.list("path_to_directory").parallel()
会将流转换为 parallelStream
。【参考方案2】:
您可以使用FileVisitor 类一次遍历和读取一个文件。这样你就不会得到OOM错误。
使用Files.html#walkFileTree方法访问目录下的文件。
一般示例如下。
Path path = FileSystems.getDefault().getPath("D:\\path\\with\\lots\\of\\files");
Files.walkFileTree(path, new FileVisitor<Path>()
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
return FileVisitResult.CONTINUE;
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
// here you have the files to process
System.out.println(file);
return FileVisitResult.CONTINUE;
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException
return FileVisitResult.TERMINATE;
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException
return FileVisitResult.CONTINUE;
);
这里是Java SE tutorial 的链接。 更多示例,请参考here。
【讨论】:
以上是关于从文件夹中读取 Java 流中的块中的文件名的主要内容,如果未能解决你的问题,请参考以下文章
如何从 Python 中的文件/流中懒惰地读取多个 JSON 值?