我有一个包含超过10万个文件夹的文件夹。如果我使用listFiles(),那么它需要花费很多时间,因为它返回文件夹中的所有条目。我想要的是,我将处理的文件夹中的随机条目,并将移动到不同的位置。
答案 0 :(得分:0)
我很想知道listFiles()
会带来什么样的表现,所以我进行了测试。有100,000名儿童,我看到延迟0.051秒。你可能会看到这个速度保持相对较好(我发现的任何内容都不会暗示Java中的任何实质性增长;任何快速降级都将在本地出现)。虽然这种延迟相对较小,但我查看了 listFiles
的工作方式,以确定是否可以进行任何潜在的改进。
第一种解决方案是使用File.list()
而不是File.listFiles()
。如果查看listFiles()
方法的代码,可以看到Java如何找到文件夹的子代。
public File[] listFiles() {
String[] ss = list();
if (ss == null) return null;
int n = ss.length;
File[] fs = new File[n];
for (int i = 0; i < n; i++) {
fs[i] = new File(ss[i], this);
}
return fs;
}
listFiles()
方法获取子项名称的数组,即字符串,并为每个子项创建一个File对象。 File对象的迭代和实例化将为您的任务创建不必要的无意中;如果忽略从String[]
到File[]
的转换,您只需要一个较便宜的文件。幸运的是,list();
方法是公共的,因此您可以使用此方法来获得轻微的性能提升。
粗略测试表明,这会将时间缩短约25%(搜索包含100,000名儿童的文件夹时)。
下一个合乎逻辑的步骤是查看list()
并查看它的作用。事情变得有点粘:
public String[] list() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(path);
}
if (isInvalid()) {
return null;
}
return fs.list(this);
}
假设您可以跳过安全性和验证检查,您可以按fs.list(this);
跟踪它所需的位置。接下来,你会看到一个兔子洞:
fs.list(this)
DefaultFileSystem.getFileSystem().list(File f)
new WinNTFileSystem.list(File f)
这是你停下来的地方。 .list(文件f)is declared
native`意味着它已使用JNI在本机代码中实现。一路下线访问受到限制意味着
如果您想尽可能深入,可以使用反射来访问这些方法。我相信你可以使用的最低级别是本机方法WinNTFileSystem.file(File f)
,但我强烈建议不要这样做。
/* Setup */
// Get FileSystem from File class
Field fieldFileSystem = File.class.getDeclaredField("fs");
fieldFileSystem.setAccessible(true);
Object fs = fieldFileSystem.get(null);
// Get WinNTFileSystem class
Class<?> classWinNTFileSystem = Class.forName("java.io.WinNTFileSystem");
// Get native `list` method from WinNTFileSystem class
Method methodList = classWinNTFileSystem .getMethod("list", File.class);
methodList.setAccessible(true);
/* Each time you want to invoke the method */
String[] files = (String[]) methodList.invoke(fs, root);
此次性能升级差异很大。有时我看到比使用前一种方法略好,而其他人看到了超过50%的显着改善,尽管我对这种表现持怀疑态度。使用此方法,您应该看到至少略微增加File.list()
。 (假设您只创建一次Method对象并通过代码重用它。)
使用密钥不是文件名,您不会发现任何显着的性能提升超出我的显示。为了索引文件,你需要,你需要列表,因为没有本地实现&#34; get child at index n&#34;。您可以使用密钥或索引作为文件名本身,只需使用new File(root, "12353");
创建一个新的File对象。
答案 1 :(得分:0)
实际上java具有DirectoryStream接口可用于迭代目录而无需将其内容预加载到内存中。下面提到了相同的示例代码。
Path logFolder = Paths.get(windowsClientParentFolder);
try (DirectoryStream<Path> stream = Files.newDirectoryStream(logFolder)) {
for (Path entry : stream) {
String folderName = entry.getFileName().toString();
//process the folder
}
} catch (IOException ex) {
System.out.println("Exception occurred while reading folders.");
}