我的应用程序扫描文件系统的一部分,用户报告扫描网络驱动器时非常慢。通过测试我的代码,我确定了瓶颈:方法File.isFile()
,File.isDirectory()
和File.isHidden()
都在调用fs.getBooleanAttributes(File f)
。这个方法在Windows网络驱动器上似乎非常缓慢。如何提高性能?我是否可以以某种方式避免调用此方法?
我的应用程序扫描文件系统的一部分,用户报告扫描网络驱动器时非常慢。通过测试我的代码,我确定了瓶颈:方法File.isFile()
,File.isDirectory()
和File.isHidden()
都在调用fs.getBooleanAttributes(File f)
。这个方法在Windows网络驱动器上似乎非常缓慢。如何提高性能?我是否可以以某种方式避免调用此方法?
防御性编程经常会调用那些isXYZ()
方法,这通常是一个好的实践。但是,有时候性能会很差,就像你发现的那样。
另一种方法是假设文件是存在的、可见的、可读的等等,并尝试读取它。如果不符合条件,就会抛出异常,您可以捕获异常,然后进行检查以找出出了什么问题。这样,您就可以针对通用情况进行优化(即一切正常),只有在发生错误时才执行较慢的操作。
listFiles
和isDirectory
遍历目录树的代码示例,下面是优化前后的对比结果(我的代码使用了一个通用回调函数来处理每个文件夹和文件,如果我在编写C#代码,这将是一个委托)。listFiles
方法实际上更加简洁易懂,而且在本地硬盘(950毫秒 vs 1000毫秒)和局域网驱动器(26秒 vs 28秒)上都稍微快一些,这两种情况下处理的文件数为23,000个。static public int processDirectory(File dir, Callback cbk, FileSelector sel) {
dir=dir.getAbsoluteFile();
return _processDirectory(dir.getParentFile(),dir,new Callback.WithParams(cbk,2),sel);
}
static private int _processDirectory(File par, File fil, Callback.WithParams cbk, FileSelector sel) {
File[] ents=(sel==null ? fil.listFiles() : fil.listFiles(sel)); // listFiles returns null if fil is not a directory
int cnt=1;
if(ents!=null) {
cbk.invoke(fil,null);
for(int xa=0; xa<ents.length; xa++) { cnt+=_processDirectory(fil,ents[xa],cbk,sel); }
}
else {
cbk.invoke(par,fil); // par can never be null
}
return cnt;
}
旧代码
static public int oldProcessDirectory(File dir, Callback cbk, FileSelector sel) {
dir=dir.getAbsoluteFile();
return _processDirectory(dir,new Callback.WithParams(cbk,2),sel);
}
static private int _processDirectory(File dir, Callback.WithParams cbk, FileSelector sel) {
File[] ents=(sel==null ? dir.listFiles() : dir.listFiles(sel));
int cnt=1;
cbk.invoke(dir,null);
if(ents!=null) {
for(int xa=0; xa<ents.length; xa++) {
File ent=ents[xa];
if(!ent.isDirectory()) {
cbk.invoke(dir,ent);
ents[xa]=null;
cnt++;
}
}
for(int xa=0; xa<ents.length; xa++) {
File ent=ents[xa];
if(ent!=null) {
cnt+=_processDirectory(ent,cbk,sel);
}
}
}
return cnt;
}
如果你还没有尝试过,建议自己调用getBooleanAttributes并执行必要的掩码操作,这样如果你对同一个文件进行多次检查就会更快。虽然这不是一个完美的解决方案(也会让你的代码变得与平台有关),但它可以将性能提高3到4倍。即使速度没有达到应有的水平,但仍然是相当显著的性能提升。
JDK7的java.nio.file.Path功能应该对此有所帮助。
最后,如果你在终端用户环境中有任何控制权,请建议用户配置其防病毒软件以不扫描网络驱动器。许多大型杀毒软件(不确定它们要解决什么问题)默认开启此功能。我不知道这会对各种File方法产生什么影响,但我们发现配置不正确的防病毒软件几乎会导致网络资源上的所有文件访问出现延迟问题。