事实上,有很多解决方案可以完成这项工作。我假设你想要找到目录树中与文件名匹配的
唯一文件(或第一个文件)。这是一个优化问题,因为有多种探索解决方案的方式,我们希望找到一个可接受的解决方案。
1- 使用FileUtils.listFiles的解决方案
public static File searchFileWithFileUtils(final File file, final String fileName) {
File target = null;
if(file.isDirectory()) {
Collection<File> files = FileUtils.listFiles(file, null, true);
for (File currFile : files) {
if (currFile.isFile() && currFile.getName().equals(fileName)) {
target = currFile;
break;
}
}
}
return target;
}
使用
FileUtils
库的解决方案并不适合,因为方法
FileUtils#listFiles()
会加载整个目录/文件夹树(代价很高!)。
我们不需要知道整个树,可以选择更好的算法,在找到文件时停止搜索。
2- 递归解决方案
public static File searchFileRecursive(final File file, final String search) {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
File target = searchFileRecursive(f, search);
if(target != null) {
return target;
}
}
} else {
if (search.equals(file.getName())) {
return file;
}
}
return null;
}
该算法测试文件是否存在于任何文件夹中。如果不存在,则递归地尝试当前文件夹的子文件夹。如果在当前分支中未找到文件,则尝试另一个子文件夹。
探索是深入的,对于深度为1的任何文件,算法将探索先前子文件夹的全部内容(之前的分支完全被探索!)。该算法在第一分支内深处的文件具有最佳性能。
在大多数情况下,文件位置不深,因此让我们探索另一种在大多数情况下工作的算法。
3- 最快解决方案:按深度进行探索
public static File searchFileByDeepness(final String directoryName, final String fileName) {
File target = null;
if(directoryName != null && fileName != null) {
File directory = new File(directoryName);
if(directory.isDirectory()) {
File file = new File(directoryName, fileName);
if(file.isFile()) {
target = file;
}
else {
List<File> subDirectories = getSubDirectories(directory);
do {
List<File> subSubDirectories = new ArrayList<File>();
for(File subDirectory : subDirectories) {
File fileInSubDirectory = new File(subDirectory, fileName);
if(fileInSubDirectory.isFile()) {
return fileInSubDirectory;
}
subSubDirectories.addAll(getSubDirectories(subDirectory));
}
subDirectories = subSubDirectories;
} while(subDirectories != null && ! subDirectories.isEmpty());
}
}
}
return target;
}
private static List<File> getSubDirectories(final File directory) {
File[] subDirectories = directory.listFiles(new FilenameFilter() {
@Override
public boolean accept(final File current, final String name) {
return new File(current, name).isDirectory();
}
});
return Arrays.asList(subDirectories);
}
对于每个深度,该算法在同一级别的所有文件夹中搜索文件。如果未找到文件,则尝试下一个深度(深度 ++)。由于并行探索(对称性),这种解决方案在大多数情况下都是合适的。
比较:
public class FileLocationFinder {
public static void main(final String[] args) {
String rootFolder = args[0];
String fileName = args[1];
long start = System.currentTimeMillis();
File target = searchFileWithFileUtils(new File(rootFolder), fileName);
System.out.println(target.getAbsolutePath());
System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms");
start = System.currentTimeMillis();
target = searchFileRecursive(new File(rootFolder), fileName);
System.out.println(target.getAbsolutePath());
System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms");
start = System.currentTimeMillis();
target = searchFileByDeepness(rootFolder, fileName);
System.out.println(target.getAbsolutePath());
System.out.println("Duration: " + (System.currentTimeMillis() - start) + "ms");
}
public static File searchFileWithFileUtils(final File file, final String fileName) {
File target = null;
if(file.isDirectory()) {
Collection<File> files = FileUtils.listFiles(file, null, true);
for (File currFile : files) {
if (currFile.isFile() && currFile.getName().equals(fileName)) {
target = currFile;
break;
}
}
}
return target;
}
public static File searchFileRecursive(final File file, final String search) {
if (file.isDirectory()) {
File[] files = file.listFiles();
for (File f : files) {
File target = searchFileRecursive(f, search);
if(target != null) {
return target;
}
}
} else {
if (search.equals(file.getName())) {
return file;
}
}
return null;
}
public static File searchFileByDeepness(final String directoryName, final String fileName) {
File target = null;
if(directoryName != null && fileName != null) {
File directory = new File(directoryName);
if(directory.isDirectory()) {
File file = new File(directoryName, fileName);
if(file.isFile()) {
target = file;
}
else {
List<File> subDirectories = getSubDirectories(directory);
do {
List<File> subSubDirectories = new ArrayList<File>();
for(File subDirectory : subDirectories) {
File fileInSubDirectory = new File(subDirectory, fileName);
if(fileInSubDirectory.isFile()) {
return fileInSubDirectory;
}
subSubDirectories.addAll(getSubDirectories(subDirectory));
}
subDirectories = subSubDirectories;
} while(subDirectories != null && ! subDirectories.isEmpty());
}
}
}
return target;
}
private static List<File> getSubDirectories(final File directory) {
File[] subDirectories = directory.listFiles(new FilenameFilter() {
@Override
public boolean accept(final File current, final String name) {
return new File(current, name).isDirectory();
}
});
return Arrays.asList(subDirectories);
}
}
结果:
使用FileUtils搜索文件: 20186毫秒 | 递归搜索文件: 1134毫秒 | 按深度搜索文件: 16毫秒
[编辑] 你也可以使用Java 8的文件API来完成这个任务:
public static File searchFileJava8(final String rootFolder, final String fileName) {
File target = null;
Path root = Paths.get(rootFolder);
try (Stream<Path> stream = Files.find(root, Integer.MAX_VALUE, (path, attr) ->
path.getFileName().toString().equals(fileName))) {
Optional<Path> path = stream.findFirst();
if(path.isPresent()) {
target = path.get().toFile();
}
}
catch (IOException e) {
}
return target;
}
但执行时间并没有变得更好(994毫秒)。