De hecho, hay muchas soluciones para hacer el trabajo. Supongo que desea encontrar un archivo único (o el primero) que se encuentra en un árbol de directorios que coincide con el nombre de archivo. Es un problema de optimización porque hay varias formas de explorar soluciones y queremos encontrar una solución aceptable.
1- solución utilizando 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;
}
la solución usando la biblioteca FileUtils
no es una solución adecuada porque el método FileUtils#listFiles()
cargas de todo el árbol de directorios/carpetas (el costo es caro!). No necesitamos saber todo el árbol, podemos elegir un mejor algoritmo que se detiene cuando se encuentra el archivo.
2- recursiva Solución
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;
}
Las pruebas del algoritmo si existe el archivo dentro de ninguna carpeta. Si no, intenta subcarpeta de la carpeta actual ... recursivamente. Si el archivo no se encuentra en la rama actual, intenta con otra subcarpeta.
La exploración es profunda, y para cualquier archivo con una profundidad de 1, el algoritmo explorará la totalidad de las subcarpetas anteriores (¡las ramas anteriores se exploraron por completo!). Este algoritmo tiene el mejor rendimiento para archivos en una ubicación profunda dentro de la primera rama.
En la mayoría de los casos, la ubicación del archivo no es profunda, así que deja explorar otro algoritmo que funciona en la mayoría de los casos.
3- rápido Solución: la exploración de profundidad
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);
}
Para cada profundidad, el algoritmo busca en el archivo dentro de todas las carpetas del mismo nivel. Si el archivo no se encuentra, intenta el siguiente nivel (profundidad ++). Debido a la exploración en paralelo (simetría), esta solución es adecuada en la mayoría de los casos.
comparación:
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");
}
// Solution with 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;
}
// Recursive solution
//--------------------------------------------
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;
}
// Fastest solution
//--------------------------------------------
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);
}
}
Resultados:
searchFileWithFileUtils: 20186ms | searchFileRecursive: 1134ms | searchFileByDeepness: 16ms
[EDIT] También puede utilizar Java 8 Archivos de API para hacer este trabajo:
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;
}
Pero el tiempo de ejecución no es mejor (994ms).
Aparte: puede/debería usar un "corte" regular en lugar de "romper la etiqueta" aquí. (Un retorno simple es mejor aún como señala @ chssPly76.) –