Listar subdirectorios usando flatMap
Hemos visto antes cómo enumerar archivos en un directorio específico. Echemos un vistazo a cómo atravesar los subdirectorios directos del directorio especificado (la profundidad es 1), primero implementemos una versión simple y luego usemos el método flatMap () más conveniente para implementarla.
Primero usamos un bucle for tradicional para recorrer un directorio específico. Si hay archivos en el subdirectorio, agréguelos a la lista; de lo contrario, agregue el subdirectorio a la lista. Finalmente, imprima el número total de todos los archivos. El código está a continuación: este es para el modo difícil.
Copie el código de código de la siguiente manera:
lista pública vacía estáticaTheHardWay() {
Lista<Archivo> archivos = nueva ArrayList<>();
Archivo[] filesInCurrentDir = nuevo archivo(".").listFiles();
para (archivo archivo: filesInCurrentDir) {
Archivo[] archivosInSubDir = archivo.listFiles();
if(archivosInSubDir! = nulo) {
archivos.addAll(Arrays.asList(filesInSubDir));
} demás {
archivos.add(archivo);
}
}
System.out.println("Cuenta: " + archivos.tamaño())
}
Primero obtenemos la lista de archivos en el directorio actual y luego la recorremos. Para cada archivo, si tiene subarchivos, agréguelos a la lista. No hay ningún problema con esto, pero tiene algunos problemas comunes: mutabilidad, paranoia de tipos básicos, imperatividad, verbosidad del código, etc. Un pequeño método llamado flatMap() puede resolver estos problemas.
Como su nombre indica, este método se aplana después del mapeo. Asigna elementos en una colección al igual que map(). Pero a diferencia del método map(), la expresión lambda en el método map() solo devuelve un elemento, y lo que se devuelve aquí es un objeto Stream. Entonces, este método aplana múltiples flujos y asigna cada elemento dentro a un flujo aplanado.
Podemos usar flatMap() para realizar varias operaciones, pero el problema que nos ocupa ilustra su valor. Cada subdirectorio tiene una lista o secuencia de archivos y queremos obtener la lista de archivos en todos los subdirectorios del directorio actual.
Algunos directorios pueden estar vacíos o no tener elementos secundarios. En este caso, encapsulamos el directorio o archivo vacío en un objeto de secuencia. Si queremos ignorar un archivo, el método flatMap() en el JDK también puede manejar muy bien archivos vacíos: fusionará una referencia nula en la secuencia como una colección vacía; Veamos el uso del método flatMap().
Copie el código de código de la siguiente manera:
vacío estático público mejorWay() {
Listar<Archivo> archivos =
Stream.of(nuevo archivo(".").listFiles())
.flatMap(archivo -> archivo.listFiles() == nulo?
Corriente.de(archivo) : Corriente.de(archivo.listFiles()))
.collect(toList());
System.out.println("Cuenta: " + archivos.tamaño());
}
Primero obtenemos la secuencia de subarchivos del directorio actual y luego llamamos a su método flatMap(). Luego pase una expresión lambda a este método, que devolverá una secuencia de subarchivos del archivo especificado. El método flatMap() devuelve una colección de archivos en todos los subdirectorios del directorio actual. Usamos el método Collect() y el método toList()( en Collectors para recopilarlos en una lista.
La expresión lambda que pasamos a flatMap() devuelve un subarchivo de un archivo. De lo contrario, se devuelve la secuencia del archivo. El método flatMap() asigna elegantemente esta secuencia a una colección de secuencias, luego aplana la colección y finalmente la fusiona en una sola secuencia.
El método flatMap() reduce una gran cantidad de trabajo de desarrollo: combina dos operaciones consecutivas, a menudo llamadas tuplas, en una operación elegante.
Ya sabemos cómo utilizar el método flatMap() para enumerar todos los archivos en un subdirectorio inmediato. Supervisemos las operaciones de modificación de archivos.
Monitorear modificaciones de archivos
Ya sabemos cómo buscar archivos y directorios, pero si queremos recibir mensajes rápidos cuando se crean, modifican o eliminan archivos, esto también es muy sencillo. Este mecanismo es muy útil para monitorear cambios en archivos especiales, como archivos de configuración y recursos del sistema. Exploremos esta herramienta introducida en Java 7, WatchService, que se puede utilizar para monitorear modificaciones de archivos. Muchas de las características que vemos a continuación provienen de JDK 7, pero la mayor mejora aquí es la conveniencia que brindan los iteradores internos.
Primero escribamos un ejemplo de monitoreo de modificaciones de archivos en el directorio actual. La clase Path en el JDK corresponde a una instancia en el sistema de archivos, que es una fábrica de servicios de observador. Podemos registrar eventos de notificación para este servicio, así:
Copie el código de código de la siguiente manera:
Ruta final ruta = Paths.get(".");
final WatchService watchService =
ruta.getFileSystem()
.newWatchService();
ruta.register(watchService, StandardWatchEventKinds.ENTRY_MODIFY);
System.out.println("Informe cualquier archivo modificado en el próximo minuto...");
Registramos un WatchService para observar modificaciones en el directorio actual. Puede sondear este WatchService para obtener las operaciones de modificación de archivos en el directorio y nos devolverá estos cambios a través de WatchKey. Una vez que tengamos la clave, podemos recorrer todos sus eventos para obtener los detalles de la actualización del archivo. Dado que se pueden modificar varios archivos al mismo tiempo, la operación de sondeo puede devolver varios eventos. Echemos un vistazo al código de sondeo y recorrido.
Copie el código de código de la siguiente manera:
final WatchKey watchKey = watchService.poll(1, TimeUnit.MINUTES);
si (watchKey! = nulo) {
watchKey.pollEvents()
.arroyo()
.forEach(evento ->
System.out.println(event.context()));
}
Como puede ver aquí, las funciones de Java 7 y Java 8 aparecen al mismo tiempo. Convertimos la colección devuelta por pollEvents() en una secuencia Java 8 y luego usamos su iterador interno para imprimir información de actualización detallada para cada archivo.
Ejecutemos este código y luego modifiquemos el archivo sample.txt en el directorio actual para ver si el programa puede detectar esta actualización.
Copie el código de código de la siguiente manera:
Informe cualquier cambio de archivo en el próximo minuto 1...
muestra.txt
Cuando modificamos este archivo, el programa nos indicará que el archivo ha sido modificado. Podemos usar esta función para monitorear las actualizaciones de diferentes archivos y luego realizar las tareas correspondientes. Por supuesto, también podemos registrar únicamente operaciones de creación o eliminación de archivos.
Resumir
Con expresiones lambda y referencias de métodos, las tareas comunes como la manipulación de cadenas y archivos y la creación de comparadores personalizados se vuelven más fáciles y concisas. Las clases internas anónimas se vuelven elegantes y la variabilidad desaparece como la niebla de la mañana después del amanecer. Otro beneficio de codificar con este nuevo estilo es que puede utilizar las nuevas funciones del JDK para recorrer directorios grandes de manera eficiente.
Ahora ya sabe cómo crear una expresión lambda y pasarla a un método. En el próximo capítulo, presentaremos cómo utilizar interfaces funcionales y expresiones lambda para diseñar software.