Cara terbaik untuk membuat daftar file di Jawa, disortir berdasarkan Tanggal Dimodifikasi?

240

Saya ingin mendapatkan daftar file dalam direktori, tetapi saya ingin menyortirnya sehingga file terlama adalah yang pertama. Solusi saya adalah memanggil File.listFiles dan hanya menggunakan daftar berdasarkan File.lastModified, tapi saya bertanya-tanya apakah ada cara yang lebih baik.

Sunting: Solusi saya saat ini, seperti yang disarankan, adalah dengan menggunakan Pembanding anonim:

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
sumber
1
ada apa dengan bagian "lama baru" ini? kenapa kamu tidak membandingkan saja rindu itu sendiri? yang akan menghindarkan Anda dari membuat banyak rindu hanya untuk sampai ke metode compareTo ...
John Gardner
Kode ini tidak dikompilasi. bandingkan metode berharap pengembalian itu int bukan Long.
marcospereira
1
Apakah saya satu-satunya yang menganggap solusi ini gila? Anda menelepon file.lastModified()banyak sekali. Sebaiknya dapatkan semua tanggal terlebih dahulu dan pesan kemudian, sehingga file.lastModified()hanya dipanggil sekali per file.
cprcrack
1
Anda dapat menggunakan komparator apache commons:Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
jlunavtgrad
5
Ada solusi yang lebih baik dengan Java 8 (lihat jawaban viniciussss):Arrays.sort(files, Comparator.comparingLong(File::lastModified));
starbroken

Jawaban:

99

Saya pikir solusi Anda adalah satu-satunya cara yang masuk akal. Satu-satunya cara untuk mendapatkan daftar file adalah dengan menggunakan File.listFiles () dan dokumentasi menyatakan bahwa ini tidak membuat jaminan tentang urutan file yang dikembalikan. Oleh karena itu Anda perlu menulis Comparator yang menggunakan File.lastModified () dan meneruskannya, bersama dengan array file, ke Arrays.sort () .

Dan Dyer
sumber
Bagaimana cara saya memperbaiki pemformatan di sini? Terlihat bagus di pratinjau tetapi tautan ke-4 kacau.
Dan Dyer
1
File.lastModified mungkin berubah saat menyortir hasil akhir dalam Kesalahan Pelanggaran Metode Perbandingan, lihat: stackoverflow.com/questions/20431031 Lihat stackoverflow.com/a/4248059/314089 untuk solusi yang lebih baik.
icyerasor
48

Ini mungkin lebih cepat jika Anda memiliki banyak file. Ini menggunakan pola menghias-sort-undecorate sehingga tanggal modifikasi terakhir dari setiap file diambil hanya sekali daripada setiap kali algoritma sortir membandingkan dua file. Ini berpotensi mengurangi jumlah panggilan I / O dari O (n log n) ke O (n).

Ini lebih banyak kode, jadi ini hanya boleh digunakan jika Anda terutama mementingkan kecepatan dan lebih cepat terukur dalam praktiknya (yang belum saya periksa).

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
sumber
5
Jawaban terbaik, karena mungkin itu satu-satunya yang mencegah "Kesalahan Pelanggaran Metode Perbandingan" jika perubahan terakhir diubah saat menyortir?
icyerasor
1
Ini juga harus digunakan ketika Anda khawatir tidak mendapatkan IllegalArgumentException karena pelanggaran metode perbandingan. Metode menggunakan Peta akan gagal jika ada lebih dari satu file dengan nilai LastModified yang sama yang akan mengakibatkan penghapusan file-file ini. Ini pastinya jawaban yang diterima.
Pengembang Android
44

Solusi elegan sejak Java 8:

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

Atau, jika Anda menginginkannya dalam urutan menurun, balikkan saja:

File[] files = directory.listFiles();
Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
viniciussss
sumber
2
Ini benar-benar solusi termudah. Untuk daftar:files.sort(Comparator.comparingLong(File::lastModified));
starbroken
@ starbroken Solusi Anda tidak berfungsi jika file adalah array sederhana, seperti File [], yang dikembalikan oleh directory.listFiles ().
viniciussss
@ starbroken Agar solusi Anda berfungsi, Anda harus menggunakan ArrayList<File> files = new ArrayList<File>(Arrays.asList(directory.listFiles())), itu tidak lebih mudah dari sekadar File[] files = directory.listFiles().
viniciussss
Ya aku setuju denganmu. Jika Anda memiliki array file, tidak ada alasan untuk membuat daftar. (Jika ada yang bertanya-tanya, 'tambahan' ArrayList<File>(...)dalam komentar viniciussss diperlukan untuk mendapatkan daftar yang dapat diubah yang dapat diurutkan.) Saya menemukan utas ini mencari cara untuk mengurutkan daftar file. Jadi saya baru saja menambahkan kode itu sehingga orang hanya dapat menyalinnya jika mereka memiliki daftar juga.
starbroken
The Comparatorkelas tidak memiliki metode panggilancomparingLong
zeleven
37

Bagaimana dengan pendekatan yang serupa, tetapi tanpa tinju ke objek Panjang:

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
sumber
Ini tampaknya hanya API 19+.
Gábor
4
Gunakan return Long.valueOf (f1.lastModified ()). CompareTo (f2.lastModified ()); bukan untuk api yang lebih rendah.
Martin Sykes
25

Anda juga dapat melihat apache commons IO , ia memiliki komparator modifikasi terakhir dan banyak utilitas bagus lainnya untuk bekerja dengan file.

pengguna17163
sumber
5
Ada kesalahan aneh di javadoc dengan solusi ini, karena javadoc mengatakan untuk menggunakan "LastModifiedFileComparator.LASTMODIFIED_COMPARATOR.sort (daftar);" untuk mengurutkan daftar, tetapi LASTMODIFIED_COMPARATOR dinyatakan sebagai "Pembanding <File>", sehingga tidak memaparkan metode "pengurutan" apa pun.
Tristan
4
Gunakan seperti ini: tautan
cleroo
1
File.lastModified dapat berubah saat mengurutkan hasil akhir dalam Kesalahan Pelanggaran Metode Perbandingan, lihat: stackoverflow.com/questions/20431031 Lihat stackoverflow.com/a/4248059/314089 untuk solusi yang lebih baik.
icyerasor
1
love apache commons, yang menyelamatkan banyak waktu,
redDevil
16

Di Jawa 8:

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

Hasen
sumber
13

Impor:

org.apache.commons.io.comparator.LastModifiedFileComparator

Apache Commons

Kode:

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
sumber
Tidak jelas instan dari mana LastModifiedFileComparator.LASTMODIFIED_COMPARATOR diambil. Mungkin menambahkan tautan ke apache commons io akan membantu.
broadband
Selesai, Terima kasih broadband
Balaji Boggaram Ramanarayan
10

Jika file yang Anda sortir dapat dimodifikasi atau diperbarui pada saat yang sama, sortir dilakukan:


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


Kedua solusi ini membuat struktur data peta sementara untuk menghemat waktu modifikasi terakhir konstan untuk setiap file dalam direktori. Alasan kami perlu melakukan ini adalah bahwa jika file Anda sedang diperbarui atau dimodifikasi saat pengurutan Anda dilakukan maka komparator Anda akan melanggar persyaratan transitivitas dari kontrak umum antarmuka komparator karena waktu modifikasi terakhir dapat berubah selama perbandingan.

Jika, di sisi lain, Anda tahu file tidak akan diperbarui atau diubah selama pengurutan Anda, Anda bisa lolos dengan cukup banyak jawaban lain yang dikirimkan ke pertanyaan ini, yang sebagian saya:

Java 8+ (Tidak ada modifikasi bersamaan saat penyortiran)

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

Catatan: Saya tahu Anda dapat menghindari terjemahan ke dan dari objek File dalam contoh di atas dengan menggunakan File :: getLastModifiedTime api dalam operasi aliran yang diurutkan, namun, maka Anda harus berurusan dengan pengecualian IO yang dicentang di dalam lambda Anda yang selalu menyusahkan. . Saya akan mengatakan jika kinerjanya cukup kritis sehingga terjemahannya tidak dapat diterima maka saya akan berurusan dengan IOException yang dicentang di lambda dengan menyebarkannya sebagai UncheckedIOException atau saya akan mengabaikan api File sama sekali dan hanya berurusan dengan objek File:

final List<File> sorted = Arrays.asList(new File(directoryPathString).listFiles());
sorted.sort(Comparator.comparing(File::lastModified));
Matthew Madson
sumber
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
sumber
1
Ini sebenarnya tidak mengurutkan pada tanggal properti yang dimodifikasi yang disebutkan dalam pertanyaan. Fungsi sortir akan menggunakan pengurutan alami dari objek File yang merupakan lexicographic yang bergantung pada sistem pada nama path .
Matt Chan
2
Collections.sort(listFiles, new Comparator<File>() {
        public int compare(File f1, File f2) {
            return Long.compare(f1.lastModified(), f2.lastModified());
        }
    });

di mana listFileskoleksi semua file di ArrayList

Anand Savjani
sumber
1

Anda dapat mencoba Pemesanan jambu biji :

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

Anda dapat menggunakan perpustakaan 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 + "");
        }
Vara
sumber
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
sumber
0

Saya datang ke pos ini ketika saya mencari masalah yang sama tetapi di android. Saya tidak mengatakan ini adalah cara terbaik untuk mendapatkan file yang diurutkan berdasarkan tanggal modifikasi terakhir, tetapi ini adalah cara termudah yang saya temukan.

Kode di bawah ini dapat membantu seseorang-

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
    }

Terima kasih

Hirdesh Vishwdewa
sumber
Tapi siapa yang menyortir?
DAB
di bagian inisialisasi dari forlingkaran Anda dapat melihat saya telah mengambil list.length-1upto i >=0yang hanya iterate Anda dalam urutan terbalik.
Hirdesh Vishwdewa
0

Ada cara yang sangat mudah dan nyaman untuk menangani masalah tanpa pembanding tambahan. Hanya kode tanggal yang diubah ke dalam String dengan nama file, urutkan, dan kemudian lepas lagi.

Gunakan String dengan panjang tetap 20, masukkan tanggal yang dimodifikasi (panjang) ke dalamnya, dan isi dengan nol di depannya. Kemudian tambahkan saja nama file ke string ini:

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

Apa yang terjadi adalah ini di sini:

Nama file1: C: \ data \ file1.html Terakhir Dimodifikasi: 1532914451455 Dimodifikasi Terakhir 20 Digit: 00000001532914451455

Nama file1: C: \ data \ file2.html Terakhir Dimodifikasi: 1532918086822 Terakhir Dimodifikasi 20 Digit: 00000001532918086822

mengubah nama file menjadi:

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

Namafile2: 00000001532918086822C: \ data \ file2.html

Anda kemudian dapat mengurutkan daftar ini.

Yang perlu Anda lakukan adalah mengupas 20 karakter lagi nanti (di Java 8, Anda dapat menghapusnya untuk seluruh Array hanya dengan satu baris menggunakan fungsi .replaceAll)

pengguna4378029
sumber
-1

Ada juga cara yang sama sekali berbeda yang mungkin lebih mudah, karena kita tidak berurusan dengan jumlah besar.

Alih-alih menyortir seluruh array setelah Anda mengambil semua nama file dan tanggal modifikasi terakhir, Anda bisa memasukkan setiap nama file saja setelah Anda mengambilnya di posisi yang tepat dari daftar.

Anda dapat melakukannya seperti ini:

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

Setelah Anda menambahkan object2 ke posisi 2, itu akan memindahkan object3 ke posisi 3.

pengguna4378029
sumber