Il modo migliore per elencare i file in Java, ordinati per data di modifica?


240

Voglio ottenere un elenco di file in una directory, ma voglio ordinarlo in modo che i file più vecchi siano i primi. La mia soluzione era quella di chiamare File.listFiles e semplicemente ricorrere all'elenco basato su File.lastModified, ma mi chiedevo se ci fosse un modo migliore.

Modifica: la mia soluzione attuale, come suggerito, è utilizzare un comparatore anonimo:

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
che ne è della parte "nuova lunga" di questo? perché non confronti solo i desideri stessi? che ti eviterebbe di creare tonnellate di desideri solo per arrivare al metodo compareTo ...
John Gardner,

Questo codice non viene compilato. i metodi di confronto prevedono che il ritorno sia un int anziché un Long.
marcospereira,

1
Sono l'unico che considera questa soluzione pazza? Stai chiamando file.lastModified()un gran numero di volte. Meglio ottenere prima tutte le date e ordinarle successivamente, in modo che file.lastModified()venga chiamato una sola volta per file.
cprcrack,

1
Puoi usare il comparatore di comuni apache:Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
jlunavtgrad

5
Esiste una soluzione migliore con Java 8 (vedi la risposta di viniciussss):Arrays.sort(files, Comparator.comparingLong(File::lastModified));
Starbroken il

Risposte:


99

Penso che la tua soluzione sia l'unico modo sensato. L'unico modo per ottenere l'elenco dei file è utilizzare File.listFiles () e la documentazione afferma che ciò non fornisce alcuna garanzia sull'ordine dei file restituiti. Pertanto è necessario scrivere un comparatore che utilizza File.lastModified () e passare questo, insieme alla matrice di file, a Arrays.sort () .


Come posso correggere la formattazione qui? Sembra benissimo nell'anteprima ma il 4 ° link è avvitato.
Dan Dyer,

1
File.lastModified potrebbe cambiare durante l'ordinamento del risultato finale in un errore di violazione del metodo di confronto, consultare stackoverflow.com/questions/20431031 Consultare stackoverflow.com/a/4248059/314089 per una possibile soluzione migliore.
Icyerasor,

48

Questo potrebbe essere più veloce se hai molti file. Questo utilizza il modello decorate-ordina-undecorate in modo che la data dell'ultima modifica di ciascun file venga recuperata solo una volta anziché ogni volta che l'algoritmo di ordinamento confronta due file. Ciò potenzialmente riduce il numero di chiamate I / O da O (n log n) a O (n).

È più codice, tuttavia, quindi dovrebbe essere usato solo se sei principalmente interessato alla velocità ed è misurabile in pratica in modo più veloce (cosa che non ho verificato).

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
La migliore risposta, poiché è probabilmente l'unica che impedisce un "Errore di violazione del metodo di confronto" se l'ultimo Modificato cambia durante l'ordinamento?
Icyerasor,

1
Questo dovrebbe essere usato anche quando non sei interessato a non ricevere IllegalArgumentException a causa della violazione del metodo di confronto. Il metodo che utilizza Mappa non riuscirà se è presente più di un file con lo stesso valore lastModified che comporterebbe l'omissione di questi file. Questa sicuramente dovrebbe essere una risposta accettata.
Sviluppatore Android

44

Soluzione elegante da Java 8:

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

Oppure, se lo vuoi in ordine decrescente, basta invertirlo:

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

2
Questa è davvero la soluzione più semplice. Per gli elenchi:files.sort(Comparator.comparingLong(File::lastModified));
stella spezzata il

@starbroken La soluzione non funziona se i file sono un array semplice, come File [], che viene restituito da directory.listFiles ().
Viniciussss,

@starbroken Affinché la tua soluzione funzioni, è necessario utilizzarla ArrayList<File> files = new ArrayList<File>(Arrays.asList(directory.listFiles())), non è più semplice di un semplice File[] files = directory.listFiles().
Viniciussss,

Si sono d'accordo con te. Se si dispone di una matrice di file, non vi è alcun motivo per creare un elenco. (Se qualcuno si chiede, quel "aggiuntivo" ArrayList<File>(...)nel commento di viniciussss è necessario per ottenere un elenco mutabile che può essere ordinato.) Ho trovato questo thread alla ricerca di un modo per ordinare un elenco di file. Quindi ho appena aggiunto quel codice in modo che le persone possano semplicemente copiarlo se hanno anche degli elenchi.
Starbroken il

La Comparatorclasse non ha alcuna chiamata di metodocomparingLong
zeleven,

37

Che dire di un approccio simile, ma senza boxe per gli oggetti Long:

File[] files = directory.listFiles();

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

Questo sembra essere solo API 19+.
Gábor,

4
Utilizzare return Long.valueOf (f1.lastModified ()). CompareTo (f2.lastModified ()); invece per api inferiori.
Martin Sykes,

25

Potresti anche guardare apache commons IO , ha un comparatore integrato nell'ultima modifica e molte altre belle utilità per lavorare con i file.


5
C'è uno strano errore in javadoc con questa soluzione, perché javadoc dice di usare "LastModifiedFileComparator.LASTMODIFIED_COMPARATOR.sort (list);" per ordinare un elenco, ma LASTMODIFIED_COMPARATOR è dichiarato come "Comparator <File>", quindi non espone alcun metodo di "ordinamento".
Tristan,

4
Usalo in questo modo: link
cleroo

1
File.lastModified potrebbe cambiare durante l'ordinamento del risultato finale in un errore di violazione del metodo di confronto, consultare stackoverflow.com/questions/20431031 Consultare stackoverflow.com/a/4248059/314089 per una possibile soluzione migliore.
Icyerasor,

1
Adoro gli Apache Commons, che hanno risparmiato molto tempo,
RedDevil,

16

In Java 8:

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


13

Importazioni:

org.apache.commons.io.comparator.LastModifiedFileComparator

Apache Commons

Codice :

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

    }

Non è chiaro da dove viene preso LastModifiedFileComparator.LASTMODIFIED_COMPARATOR. Forse l'aggiunta di un link ai comuni di Apache io sarebbe d'aiuto.
banda larga

Fatto, grazie a banda larga
Balaji Boggaram Ramanarayan,

10

Se i file che stai ordinando possono essere modificati o aggiornati contemporaneamente, l'ordinamento viene eseguito:


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


Entrambe queste soluzioni creano una struttura di dati della mappa temporanea per salvare un tempo di ultima modifica costante per ogni file nella directory. La ragione per cui dobbiamo fare questo è che se i tuoi file vengono aggiornati o modificati mentre il tuo ordinamento viene eseguito, il tuo comparatore violerà i requisiti di transitività del contratto generale dell'interfaccia del comparatore perché gli ultimi tempi modificati potrebbero cambiare durante il confronto.

Se, d'altra parte, sai che i file non verranno aggiornati o modificati durante il tuo ordinamento, puoi scappare praticamente con qualsiasi altra risposta inviata a questa domanda, di cui sono parziale a:

Java 8+ (nessuna modifica simultanea durante l'ordinamento)

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: so che puoi evitare la traduzione da e verso gli oggetti File nell'esempio precedente usando Files :: getLastModifiedTime api nell'operazione di flusso ordinata, tuttavia, devi affrontare le eccezioni IO verificate all'interno della tua lambda che è sempre una seccatura . Direi che se le prestazioni sono abbastanza critiche da rendere inaccettabile la traduzione, allora mi occuperei della IOException selezionata nella lambda propagandola come UncheckedIOException o rinuncerei del tutto all'API Files e gestirò solo gli oggetti 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
Questo in realtà non ordina la data di modifica della proprietà menzionata nella domanda. La funzione di ordinamento utilizzerà l'ordinamento naturale dell'oggetto File, che è il lessicografico dipendente dal sistema sul nome del percorso .
Matt Chan,

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

dove si listFilestrova la raccolta di tutti i file in ArrayList


1

Puoi provare l' ordinazione guava :

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

Puoi usare la libreria 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

Sono venuto a questo post quando stavo cercando lo stesso problema ma in android. Non dico che questo è il modo migliore per ottenere i file ordinati per data dell'ultima modifica, ma è il modo più semplice che ho ancora trovato.

Il codice seguente può essere utile a qualcuno-

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
    }

Grazie


Ma chi fa lo smistamento?
DAB,

nella parte inizializzazione del forciclo potete vedere ho preso list.length-1fino a i >=0che semplicemente iterate si in ordine inverso.
Hirdesh Vishwdewa,

0

Esiste un modo molto semplice e conveniente per gestire il problema senza alcun comparatore aggiuntivo. Basta codificare la data modificata nella stringa con il nome file, ordinarla e successivamente rimuoverla di nuovo.

Usa una stringa di lunghezza fissa 20, inserisci la data modificata (lunga) e riempila con zeri iniziali. Quindi aggiungi il nome file a questa stringa:

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

Quello che succede è questo qui:

Nome file1: C: \ data \ file1.html Ultima modifica: 1532914451455 Ultima modifica 20 cifre: 00000001532914451455

Nome file1: C: \ data \ file2.html Ultima modifica: 1532918086822 Ultima modifica 20 cifre: 00000001532918086822

trasforma i nomi dei file in:

Nome file1: 00000001532914451455C: \ data \ file1.html

Nome file2: 00000001532918086822C: \ data \ file2.html

È quindi possibile ordinare questo elenco.

Tutto quello che devi fare è rimuovere nuovamente i 20 caratteri in un secondo momento (in Java 8, puoi rimuoverlo per l'intero array con una sola riga usando la funzione .replaceAll)


-1

C'è anche un modo completamente diverso che potrebbe essere ancora più semplice, poiché non ci occupiamo di grandi numeri.

Invece di ordinare l'intero array dopo aver recuperato tutti i nomi file e le date lastModified, è possibile inserire ogni singolo nome file subito dopo averlo recuperato nella posizione corretta dell'elenco.

Puoi farlo in questo modo:

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

Dopo aver aggiunto object2 in posizione 2, sposta object3 in posizione 3.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.