¿La mejor manera de listar archivos en Java, ordenados por Fecha de modificación?

240

Quiero obtener una lista de archivos en un directorio, pero quiero ordenarla de modo que los archivos más antiguos sean los primeros. Mi solución fue llamar a File.listFiles y simplemente recurrir a la lista basada en File.lastModified, pero me preguntaba si había una mejor manera.

Editar: mi solución actual, como se sugiere, es usar un comparador anónimo:

File[] files = directory.listFiles();

Arrays.sort(files, new Comparator<File>(){
    public int compare(File f1, File f2)
    {
        return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
    } });
Cwick
fuente
1
¿Qué pasa con la parte "nueva larga" de esto? ¿Por qué no solo comparas los largos? eso evitaría que crees toneladas de largos solo para llegar al método compareTo ...
John Gardner
Este código no se compila. los métodos de comparación esperan que el retorno sea un int en lugar de un Long.
marcospereira
1
¿Soy el único que considera esta solución una locura? Estás llamando file.lastModified()una gran cantidad de veces. Mejor obtenga todas las fechas primero y ordene después, para que file.lastModified()solo se llame una vez por archivo.
cprcrack
1
Puede usar el comparador de apache commons:Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
jlunavtgrad
55
Hay una mejor solución con Java 8 (ver la respuesta de viniciussss):Arrays.sort(files, Comparator.comparingLong(File::lastModified));
starbroken

Respuestas:

99

Creo que su solución es la única forma sensata. La única forma de obtener la lista de archivos es usar File.listFiles () y la documentación indica que esto no garantiza el orden de los archivos devueltos. Por lo tanto, debe escribir un Comparador que use File.lastModified () y pasar esto, junto con la matriz de archivos, a Arrays.sort () .

Dan Dyer
fuente
¿Cómo arreglo el formato aquí? Se ve bien en la vista previa, pero el cuarto enlace está atornillado.
Dan Dyer,
1
File.lastModified puede cambiar al ordenar el resultado final en un error de violación del método de comparación, consulte: stackoverflow.com/questions/20431031 Consulte stackoverflow.com/a/4248059/314089 para una posible solución mejor.
icyerasor
48

Esto podría ser más rápido si tiene muchos archivos. Esto usa el patrón decorate-sort-undecorate para que la última fecha de modificación de cada archivo se recupere solo una vez lugar de cada vez que el algoritmo de clasificación compara dos archivos. Potencialmente, esto reduce la cantidad de llamadas de E / S de O (n log n) a O (n).

Sin embargo, es más código, por lo que solo se debe usar si le preocupa principalmente la velocidad y es mucho más rápido en la práctica (lo cual no he comprobado).

class Pair implements Comparable {
    public long t;
    public File f;

    public Pair(File file) {
        f = file;
        t = file.lastModified();
    }

    public int compareTo(Object o) {
        long u = ((Pair) o).t;
        return t < u ? -1 : t == u ? 0 : 1;
    }
};

// Obtain the array of (file, timestamp) pairs.
File[] files = directory.listFiles();
Pair[] pairs = new Pair[files.length];
for (int i = 0; i < files.length; i++)
    pairs[i] = new Pair(files[i]);

// Sort them by timestamp.
Arrays.sort(pairs);

// Take the sorted pairs and extract only the file part, discarding the timestamp.
for (int i = 0; i < files.length; i++)
    files[i] = pairs[i].f;
Jason Orendorff
fuente
55
La mejor respuesta, ya que es probablemente la única que evita un "Error de violación del método de comparación" si el último Modificado cambia durante la ordenación.
icyerasor
1
Esto también debe usarse cuando le preocupa no obtener IllegalArgumentException debido a una violación del método de comparación. El método que usa Map fallará si hay más de un archivo con el mismo valor LastModified que resultaría en la omisión de estos archivos. Esta definitivamente debería ser una respuesta aceptada.
Desarrollador de Android
44

Solución elegante desde Java 8:

File[] files = directory.listFiles();
Arrays.sort(files, Comparator.comparingLong(File::lastModified));

O, si lo desea en orden descendente, simplemente inviértalo:

File[] files = directory.listFiles();
Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
viniciussss
fuente
2
Esta es realmente la solución más fácil. Para las listas:files.sort(Comparator.comparingLong(File::lastModified));
starbroken
@starbroken Su solución no funciona si los archivos son una matriz simple, como File [], que es devuelta por directory.listFiles ().
viniciussss
@starbroken Para que su solución funcione, uno necesita usarla ArrayList<File> files = new ArrayList<File>(Arrays.asList(directory.listFiles())), eso no es más fácil que simplemente File[] files = directory.listFiles().
viniciussss
Sí estoy de acuerdo con usted. Si tiene una matriz de archivos, no hay razón para crear una lista. (Si alguien se pregunta, ArrayList<File>(...)se necesita ese 'adicional' en el comentario de viniciussss para obtener una lista mutable que se pueda ordenar). Encontré este hilo buscando una manera de ordenar una lista de archivos. Así que acabo de agregar ese código para que las personas simplemente puedan copiarlo si tienen listas también.
roto el
La Comparatorclase no tiene ninguna llamada a métodocomparingLong
zeleven
37

¿Qué pasa con un enfoque similar, pero sin boxeo a los objetos largos:

File[] files = directory.listFiles();

Arrays.sort(files, new Comparator<File>() {
    public int compare(File f1, File f2) {
        return Long.compare(f1.lastModified(), f2.lastModified());
    }
});
PhannGor
fuente
Esto parece ser solo API 19+.
Gábor
44
Utilice return Long.valueOf (f1.lastModified ()). CompareTo (f2.lastModified ()); en cambio para la API más baja.
Martin Sykes
25

También puede mirar Apache commons IO , tiene un comparador modificado modificado por última vez y muchas otras buenas utilidades para trabajar con archivos.

usuario17163
fuente
55
Hay un error extraño en javadoc con esta solución, porque javadoc dice que use "LastModifiedFileComparator.LASTMODIFIED_COMPARATOR.sort (list);" para ordenar una lista, pero LASTMODIFIED_COMPARATOR se declara como "Comparador <Archivo>", por lo que no expone ningún método de "ordenar".
Tristán
44
Úselo así: enlace
cleroo
1
File.lastModified puede cambiar al ordenar el resultado final en un error de violación del método de comparación, consulte: stackoverflow.com/questions/20431031 Consulte stackoverflow.com/a/4248059/314089 para una posible solución mejor.
icyerasor
1
love apache commons, que ahorró mucho tiempo,
redDevil
16

En Java 8:

Arrays.sort(files, (a, b) -> Long.compare(a.lastModified(), b.lastModified()));

Hasen
fuente
13

Importaciones:

org.apache.commons.io.comparator.LastModifiedFileComparator

Apache Commons

Código:

public static void main(String[] args) throws IOException {
        File directory = new File(".");
        // get just files, not directories
        File[] files = directory.listFiles((FileFilter) FileFileFilter.FILE);

        System.out.println("Default order");
        displayFiles(files);

        Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
        System.out.println("\nLast Modified Ascending Order (LASTMODIFIED_COMPARATOR)");
        displayFiles(files);

        Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
        System.out.println("\nLast Modified Descending Order (LASTMODIFIED_REVERSE)");
        displayFiles(files);

    }
Balaji Boggaram Ramanarayan
fuente
No está claro al instante de dónde se toma LastModifiedFileComparator.LASTMODIFIED_COMPARATOR. Quizás ayudaría agregar un enlace a apache commons io .
banda ancha
Hecho, gracias banda ancha
Balaji Boggaram Ramanarayan
10

Si los archivos que está ordenando se pueden modificar o actualizar al mismo tiempo que se realiza la clasificación:


Java 8+

private static List<Path> listFilesOldestFirst(final String directoryPath) throws IOException {
    try (final Stream<Path> fileStream = Files.list(Paths.get(directoryPath))) {
        return fileStream
            .map(Path::toFile)
            .collect(Collectors.toMap(Function.identity(), File::lastModified))
            .entrySet()
            .stream()
            .sorted(Map.Entry.comparingByValue())
//            .sorted(Collections.reverseOrder(Map.Entry.comparingByValue()))  // replace the previous line with this line if you would prefer files listed newest first
            .map(Map.Entry::getKey)
            .map(File::toPath)  // remove this line if you would rather work with a List<File> instead of List<Path>
            .collect(Collectors.toList());
    }
}

Java 7

private static List<File> listFilesOldestFirst(final String directoryPath) throws IOException {
    final List<File> files = Arrays.asList(new File(directoryPath).listFiles());
    final Map<File, Long> constantLastModifiedTimes = new HashMap<File,Long>();
    for (final File f : files) {
        constantLastModifiedTimes.put(f, f.lastModified());
    }
    Collections.sort(files, new Comparator<File>() {
        @Override
        public int compare(final File f1, final File f2) {
            return constantLastModifiedTimes.get(f1).compareTo(constantLastModifiedTimes.get(f2));
        }
    });
    return files;
}


Ambas soluciones crean una estructura de datos de mapa temporal para ahorrar un tiempo constante de última modificación para cada archivo en el directorio. La razón por la que debemos hacer esto es que si sus archivos se actualizan o modifican mientras se realiza su clasificación, su comparador violará el requisito de transitividad del contrato general de la interfaz del comparador porque los últimos tiempos modificados pueden estar cambiando durante la comparación.

Si, por otro lado, sabe que los archivos no se actualizarán o modificarán durante su ordenación, puede salirse con casi cualquier otra respuesta enviada a esta pregunta, de la cual estoy parcial a:

Java 8+ (sin modificaciones concurrentes durante la ordenación)

private static List<Path> listFilesOldestFirst(final String directoryPath) throws IOException {
    try (final Stream<Path> fileStream = Files.list(Paths.get(directoryPath))) {
        return fileStream
            .map(Path::toFile)
            .sorted(Comparator.comparing(File::lastModified))
            .map(File::toPath)  // remove this line if you would rather work with a List<File> instead of List<Path>
            .collect(Collectors.toList());
    }
}

Nota: Sé que puede evitar la traducción hacia y desde los objetos File en el ejemplo anterior mediante el uso de Files :: getLastModifiedTime api en la operación de flujo ordenado, sin embargo, debe lidiar con las excepciones de IO comprobadas dentro de su lambda, que siempre es un problema . Diría que si el rendimiento es lo suficientemente crítico como para que la traducción sea inaceptable, entonces trataría con la IOException marcada en el lambda propagándola como UncheckedIOException o renunciaría a la API de Archivos por completo y trataría solo con objetos File:

final List<File> sorted = Arrays.asList(new File(directoryPathString).listFiles());
sorted.sort(Comparator.comparing(File::lastModified));
Matthew Madson
fuente
2
public String[] getDirectoryList(String path) {
    String[] dirListing = null;
    File dir = new File(path);
    dirListing = dir.list();

    Arrays.sort(dirListing, 0, dirListing.length);
    return dirListing;
}
Calvin Schultz
fuente
1
En realidad, esto no ordena la propiedad de fecha de modificación mencionada en la pregunta. La función de clasificación utilizará el orden natural del objeto File que es el lexicográfico dependiente del sistema en el nombre de la ruta .
Matt Chan
2
Collections.sort(listFiles, new Comparator<File>() {
        public int compare(File f1, File f2) {
            return Long.compare(f1.lastModified(), f2.lastModified());
        }
    });

donde listFilesestá la colección de todos los archivos en ArrayList

Anand Savjani
fuente
1

Puedes probar el pedido de guayaba :

Function<File, Long> getLastModified = new Function<File, Long>() {
    public Long apply(File file) {
        return file.lastModified();
    }
};

List<File> orderedFiles = Ordering.natural().onResultOf(getLastModified).
                          sortedCopy(files);
Vitalii Fedorenko
fuente
1

Puede usar la biblioteca Apache LastModifiedFileComparator

 import org.apache.commons.io.comparator.LastModifiedFileComparator;  


File[] files = directory.listFiles();
        Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
        for (File file : files) {
            Date lastMod = new Date(file.lastModified());
            System.out.println("File: " + file.getName() + ", Date: " + lastMod + "");
        }
Vikas
fuente
1
private static List<File> sortByLastModified(String dirPath) {
    List<File> files = listFilesRec(dirPath);
    Collections.sort(files, new Comparator<File>() {
        public int compare(File o1, File o2) {
            return Long.compare(o1.lastModified(), o2.lastModified());
        }
    });
    return files;
}
Jaydev
fuente
0

Llegué a esta publicación cuando estaba buscando el mismo problema pero en android . No digo que esta sea la mejor manera de ordenar los archivos por fecha de última modificación, pero es la forma más fácil que encontré hasta ahora.

El siguiente código puede ser útil para alguien

File downloadDir = new File("mypath");    
File[] list = downloadDir.listFiles();
    for (int i = list.length-1; i >=0 ; i--) {
        //use list.getName to get the name of the file
    }

Gracias

Hirdesh Vishwdewa
fuente
¿Pero quién hace la clasificación?
DAB
en la parte de inicialización del forbucle, puede ver que he tomado list.length-1hasta i >=0que simplemente lo itera en orden inverso.
Hirdesh Vishwdewa
0

Hay una manera muy fácil y conveniente de manejar el problema sin ningún comparador adicional. Simplemente codifique la fecha modificada en la Cadena con el nombre del archivo, ordénelo y luego quítelo nuevamente.

Use una cadena de longitud fija 20, coloque la fecha de modificación (larga) en ella y complete con ceros a la izquierda. Luego solo agregue el nombre del archivo a esta cadena:

String modified_20_digits = ("00000000000000000000".concat(Long.toString(temp.lastModified()))).substring(Long.toString(temp.lastModified()).length()); 

result_filenames.add(modified_20_digits+temp.getAbsoluteFile().toString());

Lo que pasa es esto aquí:

Nombre de archivo1: C: \ data \ file1.html Última modificación: 1532914451455 Última modificación de 20 dígitos: 00000001532914451455

Nombre de archivo1: C: \ data \ file2.html Última modificación: 1532918086822 Última modificación de 20 dígitos: 00000001532918086822

transforma los nombres de archivo a:

Nombre de archivo1: 00000001532914451455C: \ data \ file1.html

Nombre de archivo2: 00000001532918086822C: \ data \ file2.html

Entonces puede simplemente ordenar esta lista.

Todo lo que necesita hacer es quitar los 20 caracteres más tarde (en Java 8, puede quitarlos para toda la matriz con solo una línea usando la función .replaceAll)

usuario4378029
fuente
-1

También hay una forma completamente diferente que puede ser aún más fácil, ya que no tratamos con grandes números.

En lugar de ordenar toda la matriz después de recuperar todos los nombres de archivo y las últimas fechas modificadas, puede insertar cada nombre de archivo justo después de recuperarlo en la posición correcta de la lista.

Puedes hacerlo así:

list.add(1, object1)
list.add(2, object3)
list.add(2, object2)

Después de agregar object2 a la posición 2, moverá object3 a la posición 3.

usuario4378029
fuente