Beste Möglichkeit, Dateien in Java aufzulisten, sortiert nach Änderungsdatum?

240

Ich möchte eine Liste der Dateien in einem Verzeichnis erhalten, aber ich möchte sie so sortieren, dass die ältesten Dateien an erster Stelle stehen. Meine Lösung bestand darin, File.listFiles aufzurufen und die Liste basierend auf File.lastModified neu zu sortieren, aber ich habe mich gefragt, ob es einen besseren Weg gibt.

Bearbeiten: Meine aktuelle Lösung besteht, wie vorgeschlagen, darin, einen anonymen Komparator zu verwenden:

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
quelle
1
Was ist mit dem "neuen langen" Teil davon? warum vergleichst du nicht einfach die Longs selbst? das würde vermeiden, dass Sie Tonnen von Longs erstellen, nur um zur compareTo-Methode zu gelangen ...
John Gardner
Dieser Code wird nicht kompiliert. Vergleichsmethoden erwarten, dass die Rückgabe ein int anstelle eines Long ist.
Marcospereira
1
Bin ich der einzige, der diese Lösung für verrückt hält? Sie rufen file.lastModified()sehr oft an. Holen Sie sich besser alle Daten zuerst und bestellen Sie später, damit das file.lastModified()nur einmal pro Datei aufgerufen wird.
Cprcrack
1
Sie können Apache Commons Comparator verwenden:Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
jlunavtgrad
5
Es gibt eine bessere Lösung mit Java 8 (siehe viniciussss Antwort):Arrays.sort(files, Comparator.comparingLong(File::lastModified));
starbroken

Antworten:

99

Ich denke, Ihre Lösung ist der einzig vernünftige Weg. Die einzige Möglichkeit, die Liste der Dateien abzurufen, ist die Verwendung von File.listFiles (). In der Dokumentation wird angegeben, dass dies keine Garantie für die Reihenfolge der zurückgegebenen Dateien gibt. Daher müssen Sie einen Komparator schreiben , der File.lastModified () verwendet, und diesen zusammen mit dem Array von Dateien an Arrays.sort () übergeben .

Dan Dyer
quelle
Wie kann ich die Formatierung hier korrigieren? Sieht in der Vorschau gut aus, aber der 4. Link ist verschraubt.
Dan Dyer
1
File.lastModified kann sich ändern, während das Endergebnis zu einem Fehler bei der Verletzung der Vergleichsmethode führt. Siehe: stackoverflow.com/questions/20431031 Eine mögliche bessere Lösung finden Sie unter stackoverflow.com/a/4248059/314089 .
icyerasor
48

Dies kann schneller sein, wenn Sie viele Dateien haben. Hierbei wird das Muster "Dekorieren-Sortieren-Nichtdekorieren" verwendet, sodass das Datum der letzten Änderung jeder Datei nur einmal abgerufen wird und nicht jedes Mal, wenn der Sortieralgorithmus zwei Dateien vergleicht. Dies reduziert möglicherweise die Anzahl der E / A-Aufrufe von O (n log n) auf O (n).

Es ist jedoch mehr Code, daher sollte dieser nur verwendet werden, wenn Sie sich hauptsächlich mit Geschwindigkeit befassen und er in der Praxis messbar schneller ist (was ich nicht überprüft habe).

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
quelle
5
Beste Antwort, da es wahrscheinlich die einzige ist, die einen "Verstoß gegen die Vergleichsmethode" verhindert, wenn sich lastModified während des Sortierens ändert?
icyerasor
1
Dies sollte auch verwendet werden, wenn Sie aufgrund einer Verletzung der Vergleichsmethode keine IllegalArgumentException erhalten möchten. Die Methode mit Map schlägt fehl, wenn mehr als eine Datei mit demselben lastModified-Wert vorhanden ist, wodurch diese Dateien weggelassen werden. Dies sollte definitiv eine akzeptierte Antwort sein.
Android-Entwickler
44

Elegante Lösung seit Java 8:

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

Oder wenn Sie es in absteigender Reihenfolge möchten, kehren Sie es einfach um:

File[] files = directory.listFiles();
Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
viniciussss
quelle
2
Dies ist wirklich die einfachste Lösung. Für Listen:files.sort(Comparator.comparingLong(File::lastModified));
Starbroken
@starbroken Ihre Lösung funktioniert nicht, wenn es sich bei Dateien um ein einfaches Array wie File [] handelt, das von directory.listFiles () zurückgegeben wird.
viniciussss
@starbroken Damit Ihre Lösung funktioniert, muss sie verwendet werden ArrayList<File> files = new ArrayList<File>(Arrays.asList(directory.listFiles())), das ist nicht einfacher als nur File[] files = directory.listFiles().
viniciussss
Ja, ich stimme dir zu. Wenn Sie über ein Array von Dateien verfügen, gibt es keinen Grund, eine Liste zu erstellen. (Wenn sich jemand wundert, wird dieses 'zusätzliche' ArrayList<File>(...)in viniciussss Kommentar benötigt, um eine veränderbare Liste zu erhalten, die sortiert werden kann.) Ich fand diesen Thread auf der Suche nach einer Möglichkeit, eine Liste von Dateien zu sortieren. Also habe ich diesen Code hinzugefügt, damit die Leute ihn einfach kopieren können, wenn sie zufällig auch Listen haben.
Starbroken
Die ComparatorKlasse hat keinen MethodenaufrufcomparingLong
zeleven
37

Was ist mit einem ähnlichen Ansatz, aber ohne auf die langen Objekte zu boxen:

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
quelle
Dies scheint nur API 19+ zu sein.
Gábor
4
Verwenden Sie return Long.valueOf (f1.lastModified ()). CompareTo (f2.lastModified ()); stattdessen für niedrigere APIs.
Martin Sykes
25

Sie können sich auch Apache Commons IO ansehen. Es verfügt über einen eingebauten zuletzt geänderten Komparator und viele andere nützliche Dienstprogramme für die Arbeit mit Dateien.

user17163
quelle
5
Es gibt einen seltsamen Fehler in javadoc mit dieser Lösung, weil javadoc sagt, dass "LastModifiedFileComparator.LASTMODIFIED_COMPARATOR.sort (list)" verwendet werden soll. um eine Liste zu sortieren, aber LASTMODIFIED_COMPARATOR wird als "Comparator <Datei>" deklariert, sodass keine "sort" -Methode verfügbar gemacht wird.
Tristan
4
Verwenden Sie es wie
folgt
1
File.lastModified kann sich ändern, während das Endergebnis zu einem Fehler bei der Verletzung der Vergleichsmethode führt. Siehe: stackoverflow.com/questions/20431031 Eine mögliche bessere Lösung finden Sie unter stackoverflow.com/a/4248059/314089 .
icyerasor
1
Liebe Apache Commons, das hat viel Zeit gespart,
redDevil
16

In Java 8:

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

hasen
quelle
13

Importe:

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

    }
Balaji Boggaram Ramanarayan
quelle
Es ist nicht sofort klar, woher LastModifiedFileComparator.LASTMODIFIED_COMPARATOR stammt. Vielleicht würde das Hinzufügen eines Links zu Apache Commons Commons helfen.
Breitband
Fertig, danke Breitband
Balaji Boggaram Ramanarayan
10

Wenn die zu sortierenden Dateien gleichzeitig mit der Sortierung geändert oder aktualisiert werden können:


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


Beide Lösungen erstellen eine temporäre Kartendatenstruktur, um eine konstante letzte Änderungszeit für jede Datei im Verzeichnis zu sparen. Der Grund dafür ist, dass wenn Ihre Dateien während der Sortierung aktualisiert oder geändert werden, Ihr Komparator gegen die Transitivitätsanforderungen des Generalvertrags der Komparatorschnittstelle verstößt, da sich die zuletzt geänderten Zeiten während des Vergleichs möglicherweise ändern.

Wenn Sie andererseits wissen, dass die Dateien während Ihrer Sortierung nicht aktualisiert oder geändert werden, können Sie mit so ziemlich jeder anderen Antwort auf diese Frage davonkommen, von der ich teilweise bin:

Java 8+ (Keine gleichzeitigen Änderungen während des Sortierens)

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

Hinweis: Ich weiß, dass Sie die Übersetzung von und zu Dateiobjekten im obigen Beispiel vermeiden können, indem Sie Files :: getLastModifiedTime api in der sortierten Stream-Operation verwenden. Dann müssen Sie sich jedoch mit geprüften E / A-Ausnahmen in Ihrem Lambda befassen, was immer schmerzhaft ist . Ich würde sagen, wenn die Leistung kritisch genug ist, dass die Übersetzung nicht akzeptabel ist, würde ich entweder die überprüfte IOException im Lambda behandeln, indem ich sie als UncheckedIOException weitergebe, oder ich würde auf die Datei-API insgesamt verzichten und mich nur mit Dateiobjekten befassen:

final List<File> sorted = Arrays.asList(new File(directoryPathString).listFiles());
sorted.sort(Comparator.comparing(File::lastModified));
Matthew Madson
quelle
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
quelle
1
Dies sortiert nicht nach dem Datum der Änderung der Eigenschaft, die in der Frage erwähnt wurde. Die Sortierfunktion verwendet die natürliche Reihenfolge des Dateiobjekts, bei dem es sich um den systemabhängigen lexikografischen Pfadnamen handelt .
Matt Chan
2
Collections.sort(listFiles, new Comparator<File>() {
        public int compare(File f1, File f2) {
            return Long.compare(f1.lastModified(), f2.lastModified());
        }
    });

Wo listFilesist die Sammlung aller Dateien in ArrayList

Anand Savjani
quelle
1

Sie können versuchen, Guave Bestellung :

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
quelle
1

Sie können die Apache LastModifiedFileComparator- Bibliothek verwenden

 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
quelle
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
quelle
0

Ich bin zu diesem Beitrag gekommen, als ich nach dem gleichen Problem gesucht habe, aber in android . Ich sage nicht, dass dies der beste Weg ist, um sortierte Dateien nach dem Datum der letzten Änderung zu erhalten, aber es ist der einfachste Weg, den ich bisher gefunden habe.

Der folgende Code kann für jemanden hilfreich sein.

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
    }

Vielen Dank

Hirdesh Vishwdewa
quelle
Aber wer sortiert?
DAB
Im Initialisierungsteil der forSchleife können Sie sehen, dass ich aufgenommen list.length-1habe i >=0, um Sie einfach in umgekehrter Reihenfolge zu iterieren.
Hirdesh Vishwdewa
0

Es gibt eine sehr einfache und bequeme Möglichkeit, das Problem ohne zusätzlichen Komparator zu lösen. Codieren Sie einfach das Änderungsdatum mit dem Dateinamen in den String, sortieren Sie es und entfernen Sie es später erneut.

Verwenden Sie eine Zeichenfolge mit fester Länge 20, geben Sie das Änderungsdatum (lang) ein und füllen Sie es mit führenden Nullen. Fügen Sie dann einfach den Dateinamen an diese Zeichenfolge an:

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

Was passiert ist das hier:

Dateiname1: C: \ data \ file1.html Letzte Änderung: 1532914451455 Letzte Änderung 20 Ziffern: 00000001532914451455

Dateiname1: C: \ data \ file2.html Letzte Änderung: 1532918086822 Letzte Änderung 20 Ziffern: 00000001532918086822

wandelt Dateinamen um in:

Dateiname1: 00000001532914451455C: \ data \ file1.html

Dateiname2: 00000001532918086822C: \ data \ file2.html

Sie können diese Liste dann einfach sortieren.

Alles, was Sie tun müssen, ist, die 20 Zeichen später erneut zu entfernen (in Java 8 können Sie sie mit der Funktion .replaceAll für das gesamte Array mit nur einer Zeile entfernen).

user4378029
quelle
-1

Es gibt auch einen ganz anderen Weg, der vielleicht noch einfacher ist, da wir nicht mit großen Zahlen umgehen.

Anstatt das gesamte Array zu sortieren, nachdem Sie alle Dateinamen und lastModified-Daten abgerufen haben, können Sie einfach jeden einzelnen Dateinamen direkt nach dem Abrufen an der richtigen Position in der Liste einfügen.

Sie können es so machen:

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

Nachdem Sie Objekt2 zu Position 2 hinzugefügt haben, wird Objekt3 zu Position 3 verschoben.

user4378029
quelle