循环遍历文件列表的有效方法
我正在寻找一种有效的方法来遍历一个或多个目录中的数千个文件。
迭代目录中的文件的唯一方法似乎是File.list*()
函数。 这些函数有效地加载了某种集合中的整个文件列表,然后让用户遍历它。 就时间/内存消耗而言,这似乎是不切实际的。 我试着看看commons-io和其他类似的工具。 但它们最终都会在内部的某处调用File.list*()
。 JDK7的walkFileTree()
接近了,但我无法控制何时选择下一个元素。
我在一个目录中有超过150,000个文件,经过多次-Xms / -Xmm试运行后,我摆脱了内存溢出问题。 但是填充阵列所需的时间并没有改变。
我希望做一些类似于使用opendir()/ closedir()函数的Iterable类来根据需要延迟加载文件名。 有没有办法做到这一点?
更新:
Java 7 NIO.2支持通过java.nio.file.DirectoryStream进行文件迭代。 这是一个可迭代的类。 至于JDK6及以下版本,唯一的选择是File.list*()
方法。
以下是如何遍历目录条目而不必将它们中的159k存储在数组中的示例。 根据需要添加错误/异常/关机/超时处理。 这种技术使用辅助线程来加载一个小的阻塞队列。
用法是:
FileWalker z = new FileWalker(new File(""), 1024); // start path, queue size
Iterator<Path> i = z.iterator();
while (i.hasNext()) {
Path p = i.next();
}
这个例子:
public class FileWalker implements Iterator<Path> {
final BlockingQueue<Path> bq;
FileWalker(final File fileStart, final int size) throws Exception {
bq = new ArrayBlockingQueue<Path>(size);
Thread thread = new Thread(new Runnable() {
public void run() {
try {
Files.walkFileTree(fileStart.toPath(), new FileVisitor<Path>() {
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
return FileVisitResult.CONTINUE;
}
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
try {
bq.offer(file, 4242, TimeUnit.HOURS);
} catch (InterruptedException e) {
e.printStackTrace();
}
return FileVisitResult.CONTINUE;
}
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread.setDaemon(true);
thread.start();
thread.join(200);
}
public Iterator<Path> iterator() {
return this;
}
public boolean hasNext() {
boolean hasNext = false;
long dropDeadMS = System.currentTimeMillis() + 2000;
while (System.currentTimeMillis() < dropDeadMS) {
if (bq.peek() != null) {
hasNext = true;
break;
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return hasNext;
}
public Path next() {
Path path = null;
try {
path = bq.take();
} catch (InterruptedException e) {
e.printStackTrace();
}
return path;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
就时间/内存消耗而言,这似乎是不切实际的。
即使150,000个文件也不会占用不切实际的内存。
我希望做一些类似于使用opendir()/ closedir()函数的Iterable类来根据需要延迟加载文件名。 有没有办法做到这一点?
您需要编写或查找本机代码库才能访问这些C函数。 它可能会引入比解决问题更多的问题。 我的建议是只使用File.list()
并增加堆大小。
实际上,还有另一个不好的选择。 使用System.exec
来运行ls
命令(或等效的窗口)并编写你的迭代器来读取和解析命令输出文本。 这可以避免与使用Java本地库相关的不安。
你能否通过文件类型对你的装载进行分组以缩小批量范围?
链接地址: http://www.djcxy.com/p/58557.html上一篇: Efficient way to iterate over list of files
下一篇: Using environment variables in Monodevelop with MonoTouch