Java在遍历大型目录时性能较差,是否有解决方法?
Java在遍历大型目录时性能较差,是否有解决方法?
我正在尝试逐个处理存储在网络上的文件。由于缓冲机制,读取文件很快,所以并不是问题。我遇到的问题只是列出文件夹中的目录。每个文件夹至少有10,000个文件,而且有很多文件夹。
由于File.list()返回的是一个数组而不是可迭代对象,所以性能非常慢。Java会去收集文件夹中的所有名称,并将其打包成数组后再返回。
这个问题的错误报告在http://bugs.sun.com/view_bug.do;jsessionid=db7fcf25bcce13541c4289edeb4?bug_id=4285834,并且没有解决办法。他们只是说这个问题在JDK7中已经修复。
以下是几个问题:
- 有人有解决这个性能瓶颈的方法吗?
- 我是不是在追求不可能的目标?即使只是迭代遍历目录,性能还是会很差吗?
- 我能否使用具有此功能的beta JDK7构建版本,而无需在整个项目上进行构建?
Java在遍历大型目录时性能较差的原因是由于遍历过程中的文件处理操作较慢。然而,可以通过使用`File.list(FilenameFilter filter)`方法和实现`FilenameFilter.accept(File dir, String name)`来解决这个问题。下面是一个示例代码,可以在Linux VM上运行,处理包含10,000个以上文件的目录,只需要不到10秒的时间。
import java.io.File; import java.io.FilenameFilter; public class Temp { private static void processFile(File dir, String name) { File file = new File(dir, name); System.out.println("processing file " + file.getName()); } private static void forEachFile(File dir) { String [] ignore = dir.list(new FilenameFilter() { public boolean accept(File dir, String name) { processFile(dir, name); return false; } }); } public static void main(String[] args) { long before, after; File dot = new File("."); before = System.currentTimeMillis(); forEachFile(dot); after = System.currentTimeMillis(); System.out.println("after call, delta is " + (after - before)); } }
在Windows上,列举一个包含大量文件的远程文件夹的操作,从花费超过20秒的时间降低到了500毫秒。
Java在处理大型目录时性能较差的问题可能是由于它试图像处理普通文件一样列出使用SMB协议传输的文件。为了解决这个问题,可以将文件的传输协议更改为HTTP,只获取文件名列表。这样可以通过HTTP检索文件列表,并让服务器处理文件列表。由于它将使用本地资源(服务器上的资源),因此这将非常快速。然后,在获取到文件列表后,可以按照目前的方式逐个处理文件。关键点在于在节点的另一侧拥有一个辅助机制。具体的解决方案是通过HTTP获取文件名列表,然后使用File类逐个处理文件。这样做的好处是避免了将所有文件信息都提前获取到客户端机器上的问题,只需要获取文件名即可。这可能会稍微降低处理速度,因为预获取的信息不再可用。但是,通过这种方式可以极大地提高性能。此外,设置一个仅在局域网中可见的网站来实现这种功能也是一个已经解决的问题。可以使用IIS来支持这种功能,并且可以很容易地对Windows用户进行身份验证。虽然这种解决方案可能不是完全透明的,但相比使用JNI封装本地代码并继续使用SMB协议,它更加简单和快速。
Java在遍历大型目录时性能较差的原因是因为它需要花费很长时间来获取目录列表。一个解决方法是将目录列表输出到一个文件中,然后在程序中读取该文件。另一个解决方法是使用system.exec()方法来执行命令获取目录列表,但这种方法会带来一些问题。某些情况下了一种更快的解决方法,即使用exec()方法将命令的输出重定向到一个管道中,这样可以在命令执行完成之前就开始处理文件。虽然交互可能会减慢速度,但可以尝试一下。另外,还有人提供了一个使用exec()方法来执行"ls"命令并将结果传递给程序处理的链接。然而,这些解决方法并不是很完美,有人建议最好只在没有其他选择的情况下使用它们。
下面是一个使用Java代码实现的解决方案,这个方案只适用于Windows系统,并且效率并不高,但是可以满足需求。代码中定义了一个FileProcessor抽象类,可以通过继承该类并重写processFile方法来处理每个文件。
import java.io.*; public abstract class FileProcessor { public void processFiles(String dirOptions) { Process theProcess = null; BufferedReader inStream = null; try { theProcess = Runtime.getRuntime().exec("cmd /c dir " + dirOptions); } catch(IOException e) { System.err.println("Error on exec() method"); e.printStackTrace(); } try { inStream = new BufferedReader(new InputStreamReader( theProcess.getInputStream() )); processFile(inStream.readLine()); } catch(IOException e) { System.err.println("Error on inStream.readLine()"); e.printStackTrace(); } } public abstract void processFile(String filename); }
这个解决方案适用于静态树或者很少变化的树,如果树经常发生变化,使用exec()方法重新创建文件可能是一个不错的选择,但不管怎样,这个解决方案都不是一个优雅、容错的解决方法。
总之,Java在遍历大型目录时性能较差的问题可以通过将目录列表输出到文件中然后在程序中读取该文件的方法来解决,也可以使用exec()方法来执行命令获取目录列表。然而,这些解决方法并不是非常完美,有人建议最好只在没有其他选择的情况下使用它们。