Le meilleur moyen de lister les fichiers en Java, triés par date de modification?


240

Je veux obtenir une liste de fichiers dans un répertoire, mais je veux le trier de telle sorte que les fichiers les plus anciens soient les premiers. Ma solution a été d'appeler File.listFiles et de simplement recourir à la liste basée sur File.lastModified, mais je me demandais s'il y avait une meilleure façon.

Edit: Ma solution actuelle, comme suggéré, consiste à utiliser un comparateur anonyme:

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());
    } });

1
quel est le "nouveau long" de cela? pourquoi ne comparez-vous pas simplement les longs eux-mêmes? cela vous éviterait de créer des tonnes de longs juste pour arriver à la méthode compareTo ...
John Gardner

Ce code ne compile pas. les méthodes de comparaison s'attendent à ce que le retour soit un entier au lieu d'un long.
marcospereira

1
Suis-je le seul à considérer cette solution comme folle? Vous appelez file.lastModified()énormément de fois. Mieux vaut obtenir toutes les dates en premier et commander plus tard, afin que cela file.lastModified()ne soit appelé qu'une fois par fichier.
cprcrack

1
Vous pouvez utiliser le comparateur apache commons:Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
jlunavtgrad

5
Il y a une meilleure solution avec Java 8 (voir la réponse de viniciussss):Arrays.sort(files, Comparator.comparingLong(File::lastModified));
starbroken

Réponses:


99

Je pense que votre solution est la seule solution sensée. La seule façon d'obtenir la liste des fichiers est d'utiliser File.listFiles () et la documentation indique que cela ne garantit pas l'ordre des fichiers retournés. Par conséquent, vous devez écrire un comparateur qui utilise File.lastModified () et le transmettre, avec le tableau de fichiers, à Arrays.sort () .


Comment puis-je corriger la mise en forme ici? Semble bien dans l'aperçu mais le 4ème lien est vissé.
Dan Dyer

1
File.lastModified peut changer lors du tri du résultat final dans une erreur de violation de la méthode de comparaison, voir: stackoverflow.com/questions/20431031 Voir stackoverflow.com/a/4248059/314089 pour une meilleure solution possible.
icyerasor

48

Cela pourrait être plus rapide si vous avez plusieurs fichiers. Cela utilise le modèle décorer-trier-décorer afin que la date de dernière modification de chaque fichier soit extraite une seule fois plutôt que chaque fois que l'algorithme de tri compare deux fichiers. Cela réduit potentiellement le nombre d'appels d'E / S de O (n log n) à O (n).

C'est plus de code, cependant, donc cela ne devrait être utilisé que si vous êtes principalement préoccupé par la vitesse et c'est beaucoup plus rapide en pratique (ce que je n'ai pas vérifié).

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;

5
Meilleure réponse, car c'est probablement la seule à empêcher une "erreur de violation de méthode de comparaison" si le dernier changement a été modifié lors du tri?
icyerasor

1
Cela doit également être utilisé lorsque vous ne souhaitez pas obtenir IllegalArgumentException en raison d'une violation de la méthode de comparaison. La méthode utilisant Map échouera s'il existe plusieurs fichiers avec la même valeur lastModified, ce qui entraînerait l'omission de ces fichiers. Cela devrait certainement être une réponse acceptée.
Développeur Android

44

Solution élégante depuis Java 8:

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

Ou, si vous le souhaitez dans l'ordre décroissant, inversez-le simplement:

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

2
C'est vraiment la solution la plus simple. Pour les listes:files.sort(Comparator.comparingLong(File::lastModified));
starbroken

@starbroken Votre solution ne fonctionne pas si les fichiers sont un simple tableau, comme File [], qui est renvoyé par directory.listFiles ().
viniciussss

@starbroken Pour que votre solution fonctionne, il faut l'utiliser ArrayList<File> files = new ArrayList<File>(Arrays.asList(directory.listFiles())), ce n'est pas plus simple que juste File[] files = directory.listFiles().
viniciussss

Oui je suis d'accord avec toi. Si vous disposez d'un tableau de fichiers, il n'y a aucune raison de créer une liste. (Si quelqu'un se demande, ce «supplémentaire» ArrayList<File>(...)dans le commentaire viniciussss est nécessaire pour obtenir une liste mutable qui peut être triée.) J'ai trouvé ce fil à la recherche d'un moyen de trier une liste de fichiers. J'ai donc ajouté ce code pour que les gens puissent simplement le copier s'ils ont aussi des listes.
starbroken

La Comparatorclasse n'a pas d'appel de méthodecomparingLong
zeleven

37

Qu'en est-il d'une approche similaire, mais sans encadrer les objets longs:

File[] files = directory.listFiles();

Arrays.sort(files, new Comparator<File>() {
    public int compare(File f1, File f2) {
        return Long.compare(f1.lastModified(), f2.lastModified());
    }
});

Cela semble être uniquement API 19+.
Gábor

4
Utilisez return Long.valueOf (f1.lastModified ()). CompareTo (f2.lastModified ()); à la place pour les api inférieurs.
Martin Sykes

25

Vous pouvez également consulter apache commons IO , il dispose d'un dernier comparateur modifié et de nombreux autres utilitaires agréables pour travailler avec des fichiers.


5
Il y a une étrange erreur dans javadoc avec cette solution, car javadoc dit d'utiliser "LastModifiedFileComparator.LASTMODIFIED_COMPARATOR.sort (list);" pour trier une liste, mais LASTMODIFIED_COMPARATOR est déclaré comme "Comparateur <Fichier>", donc il n'expose aucune méthode de "tri".
Tristan

4
Utilisez-le comme ceci: link
cleroo

1
File.lastModified peut changer lors du tri du résultat final dans une erreur de violation de la méthode de comparaison, voir: stackoverflow.com/questions/20431031 Voir stackoverflow.com/a/4248059/314089 pour une meilleure solution possible.
icyerasor

1
love apache commons, qui a sauvé beaucoup de temps,
redDevil

16

En Java 8:

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


13

Importations:

org.apache.commons.io.comparator.LastModifiedFileComparator

Apache Commons

Code:

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);

    }

Il n'est pas clair instantanément d'où est pris LastModifiedFileComparator.LASTMODIFIED_COMPARATOR. Peut-être que l'ajout d'un lien vers apache commons io serait utile.
haut débit

Fait, merci le haut débit
Balaji Boggaram Ramanarayan

10

Si les fichiers que vous triez peuvent être modifiés ou mis à jour en même temps que le tri est effectué:


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;
}


Ces deux solutions créent une structure de données de carte temporaire pour économiser une heure de dernière modification constante pour chaque fichier du répertoire. La raison pour laquelle nous devons le faire est que si vos fichiers sont mis à jour ou modifiés pendant que votre tri est effectué, votre comparateur violera l'exigence de transitivité du contrat général de l'interface du comparateur car les dernières heures modifiées peuvent changer pendant la comparaison.

Si, d'autre part, vous savez que les fichiers ne seront pas mis à jour ou modifiés pendant votre tri, vous pouvez vous en tirer avec à peu près n'importe quelle autre réponse soumise à cette question, dont je suis partielle:

Java 8+ (pas de modifications simultanées pendant le tri)

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());
    }
}

Remarque: Je sais que vous pouvez éviter la traduction vers et depuis les objets File dans l'exemple ci-dessus en utilisant l' API Files :: getLastModifiedTime dans l'opération de flux trié, cependant, vous devez alors traiter les exceptions d'E / S vérifiées dans votre lambda, ce qui est toujours un problème . Je dirais que si les performances sont suffisamment critiques pour que la traduction soit inacceptable, je traiterais soit l'exception IOException vérifiée dans la lambda en la propageant comme une exception UncheckedIOException, soit je renoncerais complètement à l'API Files et ne traiterais qu'avec les objets File:

final List<File> sorted = Arrays.asList(new File(directoryPathString).listFiles());
sorted.sort(Comparator.comparing(File::lastModified));

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;
}

1
Cela ne trie pas réellement la propriété de modification de date mentionnée dans la question. La fonction de tri utilisera l'ordre naturel de l'objet File, qui est le lexicographique dépendant du système sur le nom du chemin .
Matt Chan

2
Collections.sort(listFiles, new Comparator<File>() {
        public int compare(File f1, File f2) {
            return Long.compare(f1.lastModified(), f2.lastModified());
        }
    });

listFilesest la collection de tous les fichiers dans ArrayList


1

Vous pouvez essayer la commande de goyave :

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);

1

Vous pouvez utiliser la bibliothèque 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 + "");
        }

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;
}

0

Je suis venu à ce poste lorsque je cherchais le même problème, mais dans android. Je ne dis pas que c'est le meilleur moyen d'obtenir des fichiers triés par date de dernière modification, mais c'est le moyen le plus simple que j'ai trouvé à ce jour.

Le code ci-dessous peut être utile à quelqu'un-

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
    }

Merci


Mais qui fait le tri?
DAB

dans la partie initialisation de la forboucle, vous pouvez voir que j'ai pris list.length-1jusqu'à i >=0ce qui vous répète simplement dans l'ordre inverse.
Hirdesh Vishwdewa

0

Il existe un moyen très simple et pratique de gérer le problème sans comparateur supplémentaire. Codez simplement la date modifiée dans la chaîne avec le nom de fichier, triez-la, puis supprimez-la à nouveau.

Utilisez une chaîne de longueur fixe 20, mettez-y la date modifiée (longue) et remplissez-la avec des zéros non significatifs. Ensuite, ajoutez simplement le nom de fichier à cette chaîne:

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());

Qu'est-ce qui se passe ici:

Nom de fichier1: C: \ data \ file1.html Dernière modification: 1532914451455 Dernière modification à 20 chiffres: 00000001532914451455

Nom de fichier1: C: \ data \ file2.html Dernière modification: 1532918086822 Dernière modification à 20 chiffres: 00000001532918086822

transforme les noms de fichiers en:

Nom de fichier1: 00000001532914451455C: \ data \ file1.html

Nom de fichier2: 00000001532918086822C: \ data \ file2.html

Vous pouvez alors simplement trier cette liste.

Tout ce que vous devez faire est de supprimer les 20 caractères à nouveau plus tard (dans Java 8, vous pouvez les supprimer pour l'ensemble du tableau avec une seule ligne à l'aide de la fonction .replaceAll)


-1

Il existe également une manière complètement différente qui peut être encore plus facile, car nous ne traitons pas les grands nombres.

Au lieu de trier l'ensemble du tableau après avoir récupéré tous les noms de fichiers et les dates lastModified, vous pouvez simplement insérer chaque nom de fichier juste après l'avoir récupéré à la bonne position de la liste.

Vous pouvez le faire comme ceci:

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

Après avoir ajouté object2 à la position 2, il déplacera object3 à la position 3.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.