以最少的内存使用量遍历Java中的许多文件



我需要遍历一个包含大约2000万个Java文件的目录层次结构。目前我正在使用Apache Commons IO中的FileUtils.iterateFiles。这似乎是通过将整个列表加载到内存中来实现的,这很慢(延迟了应用程序启动时间),并且占用了巨大的内存(大约8GB)。我以前使用自己的递归文件迭代器,它也有同样的问题。

我一次只需要处理一个文件(或者,并行处理列表前面的一小部分文件),所以似乎没有必要浪费所有的时间和内存将一个完整的列表加载到内存中。

Java的Iterator类允许我所需要的那种占用内存最少的迭代器,但由于java.io.File类的本机特性只提供急切初始化的数组,因此利用这些迭代器似乎非常困难。

有人对我如何在不提前将文件全部加载到内存的情况下遍历文件层次结构有什么建议吗?

由于这个答案,我现在知道了新的Java7文件API,我认为这将解决我的问题,但Java7在现阶段对我来说并不是一个真正的选择。

由于Java 7 NIO不是一个选项,您可以执行"dir/B/A-D"(适用于Windows)并从输出中读取文件名。如果需要,您可以将输出重定向到一个临时文件,并从中读取文件名。

我知道这并不是你问题的严格答案,但你能不能不重新组织目录树,使用更多级别的目录,让每个目录包含更少的文件?

好吧,我最终实现了自己的迭代器来实现这一点(正如Amir所建议的)。这并不是微不足道的(尽管幸运的是,有人已经编写了使迭代器变平的代码),但它相当简单

它在内存中仍然保存着一个完整的目录列表(没有子目录),所以它不适用于平面目录布局(在这种情况下,我认为在Java7之前使用纯Java是不吉利的),但到目前为止,它对我的用例来说效果要好得多。

RecursiveFileIterable.java

import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class RecursiveFileIterable implements Iterable<File> {
private File file;
public RecursiveFileIterable(File f) {
file = f;
}
public RecursiveFileIterable(String filename) {
this(new File(filename));
}
private class DirectoriesOnlyFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
return pathname.isDirectory();
}
}
private class NoDirectoriesFilter implements FileFilter {
@Override
public boolean accept(File pathname) {
return !pathname.isDirectory();
}
}
@Override
public Iterator<File> iterator() {
List<File> normFiles = Arrays.asList(file
.listFiles(new NoDirectoriesFilter()));
ArrayList<Iterable<File>> pendingIterables = new ArrayList<Iterable<File>>();
pendingIterables.add(normFiles);
File[] subdirs = file.listFiles(new DirectoriesOnlyFilter());
for (File sd : subdirs)
pendingIterables.add(new RecursiveFileIterable(sd));
return new FlattenIterable<File>(pendingIterables).iterator();
}
}

FlattenIterable.java

// from http://langexplr.blogspot.com.au/2007/12/combining-iterators-in-java.html
import java.util.Iterator;
public class FlattenIterable<T> implements Iterable<T> {
private Iterable<Iterable<T>> iterable;
public FlattenIterable(Iterable<Iterable<T>> iterable) {
this.iterable = iterable;
}
public Iterator<T> iterator() {
return new FlattenIterator<T>(iterable.iterator());
}
static class FlattenIterator<T> implements Iterator<T> {
private Iterator<Iterable<T>> iterator;
private Iterator<T> currentIterator;
public FlattenIterator(Iterator<Iterable<T>> iterator) {
this.iterator = iterator;
currentIterator = null;
}
public boolean hasNext() {
boolean hasNext = true;
if (currentIterator == null) {
if (iterator.hasNext()) {
currentIterator = iterator.next().iterator();
} else {
return false;
}
}
while (!currentIterator.hasNext() && iterator.hasNext()) {
currentIterator = iterator.next().iterator();
}
return currentIterator.hasNext();
}
public T next() {
return currentIterator.next();
}
public void remove() {
}
}
}

最新更新