Najlepszy sposób na wyświetlanie listy plików w Javie, posortowanych według daty modyfikacji?

240

Chcę uzyskać listę plików w katalogu, ale chcę posortować ją tak, aby najstarsze pliki były pierwsze. Moim rozwiązaniem było wywołanie File.listFiles i po prostu skorzystanie z listy opartej na File.lastModified, ale zastanawiałem się, czy istnieje lepszy sposób.

Edycja: Moje obecne rozwiązanie, zgodnie z sugestią, polega na użyciu anonimowego Komparatora:

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
źródło
1
co jest z „nową długą” częścią tego? dlaczego po prostu nie porównasz samych długich? uniknęłoby to tworzenia mnóstwa tęsknot, aby dostać się do metody CompareTo ...
John Gardner
Ten kod się nie kompiluje. metody porównujące oczekują, że return jest liczbą całkowitą zamiast długiej.
marcospereira
1
Czy tylko ja uważam to rozwiązanie za szalone? Dzwonisz file.lastModified()wiele razy. Lepiej pobierz wszystkie daty najpierw, a zamów później, aby file.lastModified()wywoływać je tylko raz na plik.
cprcrack
1
Możesz użyć komparatora Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
commache
5
Jest lepsze rozwiązanie z Javą 8 (patrz odpowiedź viniciussss):Arrays.sort(files, Comparator.comparingLong(File::lastModified));
zepsuty

Odpowiedzi:

99

Myślę, że twoje rozwiązanie jest jedynym rozsądnym sposobem. Jedynym sposobem na uzyskanie listy plików jest użycie File.listFiles (), a dokumentacja stwierdza, że ​​nie daje to żadnych gwarancji co do kolejności zwracanych plików. Dlatego musisz napisać Komparator, który używa File.lastModified () i przekazać go wraz z tablicą plików do Arrays.sort () .

Dan Dyer
źródło
Jak naprawić formatowanie tutaj? Wygląda dobrze na podglądzie, ale czwarty link jest przykręcony.
Dan Dyer
1
File.lastModified może ulec zmianie podczas sortowania wyniku końcowego w wyniku błędu naruszenia metody porównania, patrz: stackoverflow.com/questions/20431031 Zobacz stackoverflow.com/a/4248059/314089, aby uzyskać lepsze rozwiązanie.
icyerasor
48

Może to być szybsze, jeśli masz wiele plików. Wykorzystuje to wzorzec dekoruj-sortuj-i dekoruj, dzięki czemu data ostatniej modyfikacji każdego pliku jest pobierana tylko raz, a nie za każdym razem, gdy algorytm sortowania porównuje dwa pliki. To potencjalnie zmniejsza liczbę wywołań We / Wy z O (n log n) do O (n).

Jest to jednak więcej kodu, więc powinno się go używać tylko wtedy, gdy martwisz się głównie szybkością, i jest to znacznie szybsze w praktyce (czego nie sprawdziłem).

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
źródło
5
Najlepsza odpowiedź, ponieważ prawdopodobnie jest to jedyna metoda zapobiegająca „błędowi naruszenia metody porównania”, jeśli ostatnia modyfikacja zmieni się podczas sortowania?
icyerasor
1
Należy tego również użyć, gdy obawiasz się, że nie otrzymasz IllegalArgumentException z powodu naruszenia metody porównywania. Metoda wykorzystująca Mapę zakończy się niepowodzeniem, jeśli istnieje więcej niż jeden plik o tej samej wartości lastModified, co spowoduje pominięcie tych plików. To zdecydowanie powinna być zaakceptowana odpowiedź.
programista Androida
44

Eleganckie rozwiązanie od Java 8:

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

Lub, jeśli chcesz to w kolejności malejącej, po prostu odwróć:

File[] files = directory.listFiles();
Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
viniciussss
źródło
2
To naprawdę najłatwiejsze rozwiązanie. W przypadku list:files.sort(Comparator.comparingLong(File::lastModified));
zepsuty
@starbroken Twoje rozwiązanie nie działa, jeśli pliki to prosta tablica, taka jak File [], zwracana przez directory.listFiles ().
viniciussss
@starbroken Aby Twoje rozwiązanie działało, musisz użyć ArrayList<File> files = new ArrayList<File>(Arrays.asList(directory.listFiles())), to nie jest łatwiejsze niż tylko File[] files = directory.listFiles().
viniciussss
Tak, zgadzam się z tobą. Jeśli masz tablicę plików, nie ma powodu, aby utworzyć listę. (Jeśli ktoś zastanawia się, ten „dodatkowy” ArrayList<File>(...)komentarz w komentarzu viniciussss jest potrzebny, aby uzyskać zmienną listę, którą można posortować.) Znalazłem ten wątek, szukając sposobu na posortowanie listy plików. Właśnie dodałem ten kod, aby ludzie mogli go po prostu skopiować, jeśli mają również listy.
zepsuty gwiazdą
ComparatorKlasa nie ma żadnego wywołanie metodycomparingLong
zeleven
37

Co z podobnym podejściem, ale bez boksu dla długich obiektów:

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
źródło
To wydaje się być tylko API 19+.
Gábor,
4
Użyj return Long.valueOf (f1.lastModified ()). CompareTo (f2.lastModified ()); zamiast tego dla niższych API.
Martin Sykes,
25

Możesz także spojrzeć na wspólne We / Wy Apache , ma on wbudowany ostatnio zmodyfikowany komparator i wiele innych przydatnych narzędzi do pracy z plikami.

użytkownik17163
źródło
5
W tym rozwiązaniu występuje dziwny błąd w javadoc, ponieważ javadoc mówi, aby użyć „LastModifiedFileComparator.LASTMODIFIED_COMPARATOR.sort (list);” aby posortować listę, ale LASTMODIFIED_COMPARATOR jest zadeklarowany jako „Komparator <Plik>”, więc nie ujawnia żadnej metody „sortowania”.
Tristan
4
Użyj tego w ten sposób: link
cleroo
1
File.lastModified może ulec zmianie podczas sortowania wyniku końcowego w wyniku błędu naruszenia metody porównania, patrz: stackoverflow.com/questions/20431031 Zobacz stackoverflow.com/a/4248059/314089, aby uzyskać lepsze rozwiązanie.
icyerasor
1
uwielbiam
wspólne apaszki
16

W Javie 8:

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

hasen
źródło
13

Importuje:

org.apache.commons.io.comparator.LastModifiedFileComparator

Apache Commons

Kod :

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
źródło
Nie wiadomo od razu, gdzie jest pobierany LastModifiedFileComparator.LASTMODIFIED_COMPARATOR. Może pomoże dodanie linku do apache commons io .
Internet szerokopasmowy
Zrobione, dzięki szerokopasmowemu internetowi
Balaji Boggaram Ramanarayan
10

Jeśli sortowane pliki można modyfikować lub aktualizować w tym samym czasie, sortowanie jest wykonywane:


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


Oba te rozwiązania tworzą tymczasową strukturę danych mapy, aby zaoszczędzić stały czas ostatniej modyfikacji każdego pliku w katalogu. Powodem, dla którego musimy to zrobić, jest to, że jeśli twoje pliki są aktualizowane lub modyfikowane podczas sortowania, twój komparator naruszy wymagania dotyczące przechodniości umowy generalnej interfejsu komparatora, ponieważ czasy ostatniej modyfikacji mogą się zmieniać podczas porównania.

Jeśli z drugiej strony wiesz, że pliki nie zostaną zaktualizowane lub zmodyfikowane podczas sortowania, możesz uciec od prawie każdej innej odpowiedzi przesłanej na to pytanie, której częściowo jestem zdania:

Java 8+ (bez jednoczesnych modyfikacji podczas sortowania)

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

Uwaga: Wiem, że możesz uniknąć tłumaczenia na i z obiektów File w powyższym przykładzie, używając Files :: getLastModifiedTime api w posortowanym strumieniu, jednak musisz poradzić sobie ze sprawdzonymi wyjątkami IO wewnątrz lambda, co zawsze jest uciążliwe . Powiedziałbym, że jeśli wydajność jest na tyle krytyczna, że ​​tłumaczenie jest niedopuszczalne, wówczas albo poradziłbym sobie ze sprawdzonym wyjątkiem IOException w lambda, propagując go jako UncheckedIOException, albo całkowicie zrezygnowałem z interfejsu API plików i zajmowałem się tylko obiektami File:

final List<File> sorted = Arrays.asList(new File(directoryPathString).listFiles());
sorted.sort(Comparator.comparing(File::lastModified));
Matthew Madson
źródło
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
źródło
1
To tak naprawdę nie sortuje według daty modyfikacji właściwości wspomnianej w pytaniu. Funkcja sortowania użyje naturalnego uporządkowania obiektu File, który jest zależną od systemu leksykograficzną nazwą ścieżki .
Matt Chan
2
Collections.sort(listFiles, new Comparator<File>() {
        public int compare(File f1, File f2) {
            return Long.compare(f1.lastModified(), f2.lastModified());
        }
    });

gdzie listFilesjest zbiorem wszystkich plików w ArrayList

Anand Savjani
źródło
1

Możesz spróbować guava Zamawianie :

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
źródło
1

Możesz użyć biblioteki 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
źródło
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
źródło
0

Przyszedłem do tego postu, gdy szukałem tego samego problemu, ale w android. Nie twierdzę, że jest to najlepszy sposób na posortowanie plików według daty ostatniej modyfikacji, ale jest to najłatwiejszy sposób, jaki znalazłem.

Poniższy kod może być komuś pomocny

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
    }

Dzięki

Hirdesh Vishwdewa
źródło
Ale kto sortuje?
DAB
w części inicjalizacji forpętli widać Brałem list.length-1zapisu do i >=0którego po prostu iterate Państwo w odwrotnej kolejności.
Hirdesh Vishwdewa
0

Istnieje bardzo łatwy i wygodny sposób rozwiązania problemu bez dodatkowego komparatora. Wystarczy zmodyfikować zmodyfikowaną datę w ciągu za pomocą nazwy pliku, posortować ją, a następnie ponownie usunąć.

Użyj ciągu o stałej długości 20, umieść w nim zmodyfikowaną datę (długą) i wypełnij wiodącymi zerami. Następnie po prostu dodaj nazwę pliku do tego ciągu:

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

Oto, co się dzieje:

Nazwa pliku1: C: \ data \ file1.html Ostatnia modyfikacja: 1532914451455 Ostatnia modyfikacja 20 cyfr: 00000001532914451455

Nazwa pliku1: C: \ data \ file2.html Ostatnia modyfikacja: 1532918086822 Ostatnia modyfikacja 20 cyfr: 00000001532918086822

przekształca nazwy plików na:

Nazwa pliku1: 00000001532914451455C: \ data \ plik1.html

Nazwa pliku2: 00000001532918086822C: \ data \ plik2.html

Następnie możesz po prostu posortować tę listę.

Wszystko, co musisz zrobić, to usunąć ponownie 20 znaków później (w Javie 8 możesz usunąć go dla całej tablicy za pomocą tylko jednego wiersza za pomocą funkcji .replaceAll)

użytkownik4378029
źródło
-1

Istnieje również zupełnie inny sposób, który może być jeszcze łatwiejszy, ponieważ nie mamy do czynienia z dużymi liczbami.

Zamiast sortować całą tablicę po pobraniu wszystkich nazw plików i dat ostatniej modyfikacji, możesz po prostu wstawić każdą pojedynczą nazwę pliku zaraz po pobraniu jej z właściwej pozycji na liście.

Możesz to zrobić w następujący sposób:

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

Po dodaniu obiektu 2 do pozycji 2 przesunie obiekt 3 do pozycji 3.

użytkownik4378029
źródło