Javaでファイルをリストするための最良の方法は、更新日でソートされていますか?


240

ディレクトリ内のファイルのリストを取得したいが、最も古いファイルが最初になるようにソートしたい。私の解決策は、File.listFilesを呼び出して、File.lastModifiedに基づいてリストを再ソートすることでしたが、もっと良い方法があるかどうか疑問に思っていました。

編集:私の現在の解決策は、提案されているように、匿名のコンパレータを使用することです:

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
これの「新しい長い」部分とは何ですか?自分たちだけでロングを比較してみませんか?それは、compareToメソッドを取得するためだけに大量のlongを作成することを回避します...
John Gardner、

このコードはコンパイルされません。compareメソッドは、戻り値がLongではなくintであることを期待しています。
marcospereira 2008年

1
私はこの解決策を狂気と考える唯一の人ですか?あなたはfile.lastModified()膨大な時間をかけています。最初にすべての日付を取得し、後で注文する方がよいので、file.lastModified()ファイルごとに1回だけ呼び出されます。
cprcrack 14

1
あなたは、Apache Commonsのコンパレータを使用することができますArrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
jlunavtgrad

5
Java 8には、より良い解決策があります(Arrays.sort(files, Comparator.comparingLong(File::lastModified));
viniciussssの

回答:


99

私はあなたの解決策が唯一の賢明な方法だと思います。ファイルのリストを取得する唯一の方法は、File.listFiles()を使用することであり、ドキュメントには、これにより返されるファイルの順序が保証されないことが記載されています。したがって、あなたは書く必要がありますコンパレータの用途があることFile.lastModifiedを()とし、ファイルの配列と一緒に、これを渡す()は、Arrays.sort


ここでフォーマットを修正するにはどうすればよいですか?プレビューでは問題ありませんが、4番目のリンクがねじ込まれています。
Dan Dyer、

1
比較法違反エラーに最終結果を並べ替えながら、見File.lastModifiedは変更される可能性があります:stackoverflow.com/questions/20431031を参照してくださいstackoverflow.com/a/4248059/314089を可能よりよい解決策のために。
icyerasor 2015年

48

多くのファイルがある場合、これはより速いかもしれません。これは、decorate-sort-undecorateパターンを使用して、各ファイルの最終変更日が1回だけフェッチされるようにします、ソートアルゴリズムが2つのファイルを比較するたびではなくます。これにより、I / O呼び出しの数がO(n log n)からO(n)に減少する可能性があります。

ただし、これはより多くのコードであるため、主に速度に関心があり、実際にはかなり高速である場合にのみ使用する必要があります(私はチェックしていません)。

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
ソート中にlastModifiedが変更された場合に「比較メソッド違反エラー」を防止する唯一の方法であるため、ベストアンサーです。
icyerasor 2015年

1
これは、比較メソッド違反が原因でIllegalArgumentExceptionが取得されないことに懸念がある場合にも使用する必要があります。同じlastModified値を持つファイルが複数ある場合、マップを使用するメソッドは失敗し、これらのファイルが省略されます。これは間違いなく受け入れられる答えになるはずです。
Androidデベロッパー

44

Java 8以降のエレガントなソリューション:

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

または、降順にしたい場合は、逆にします。

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

2
これは本当に最も簡単なソリューションです。リストの場合:files.sort(Comparator.comparingLong(File::lastModified));
スターブレイクされた2018年

@starbroken files。がFile。]のような単純な配列であり、directory.listFiles()によって返される場合、ソリューションは機能しません。
viniciussss

@starbrokenソリューションを機能させるにはArrayList<File> files = new ArrayList<File>(Arrays.asList(directory.listFiles()))、を使用する必要がありますFile[] files = directory.listFiles()
viniciussss

はい、あなたに賛成です。ファイルの配列がある場合、リストを作成する理由はありません。(誰かが不思議に思っている場合、ArrayList<File>(...)ソート可能な可変リストを取得するには、viniciussssコメントの「追加」が必要です。)このスレッドは、ファイルのリストをソートする方法を探していました。だから私はそのコードを追加しただけなので、人々がリストを持っている場合でも人々がそれを単にコピーできるようにします。
スターブレイクされた

このComparatorクラスにはメソッド呼び出しがありませんcomparingLong
zeleven

37

同様のアプローチについてはどうですか?ただし、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());
    }
});

これはAPI 19以降のみのようです。
ガボール14

4
return Long.valueOf(f1.lastModified())。compareTo(f2.lastModified());を使用します。代わりに、より低いAPIのために。
Martin Sykes

25

また、Apache Commons IOを確認することもできます。これには、最後に変更されたコンパレータと、ファイルを操作するための他の多くの便利なユーティリティが組み込まれています。


5
javadocが「LastModifiedFileComparator.LASTMODIFIED_COMPARATOR.sort(list);」を使用するように指示しているため、このソリューションではjavadocに奇妙なエラーが発生します。リストを並べ替えますが、LASTMODIFIED_COMPARATORは "Comparator <File>"として宣言されているため、 "sort"メソッドは公開されません。
トリスタン

4
次のように使用します:リンク
cleroo 2012

1
比較法違反エラーに最終結果を並べ替えながら、見File.lastModifiedは変更される可能性があります:stackoverflow.com/questions/20431031を参照してくださいstackoverflow.com/a/4248059/314089を可能よりよい解決策のために。
icyerasor

1
apache commonsを愛して、多くの時間を節約しました
redDevil

16

Java 8の場合:

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


13

輸入:

org.apache.commons.io.comparator.LastModifiedFileComparator

Apache Commons

コード:

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

    }

LastModifiedFileComparator.LASTMODIFIED_COMPARATORがどこから取得されたかはすぐにはわかりません。たぶん、apache commons ioへのリンクを追加すると役立つでしょう。
ブロードバンド

完了、ブロードバンドに感謝
Balaji Boggaram Ramanarayan 2016年

10

並べ替えを実行しているときに、並べ替え中のファイルを変更または更新できる場合:


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


これらのソリューションはどちらも、一時的なマップデータ構造を作成して、ディレクトリ内の各ファイルの最終変更時刻を一定に節約します。これが必要な理由は、並べ替えの実行中にファイルが更新または変更されている場合、比較中に最終変更時刻が変更される可能性があるため、コンパレータはコンパレータインターフェイスの一般規約の推移性要件に違反しているためです。

一方、並べ替え中にファイルが更新または変更されないことがわかっている場合は、この質問に提出された他のほとんどの回答を回避できます。

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

注:ソートされたストリーム操作でFiles :: getLastModifiedTime API を使用することで、上記の例のFileオブジェクトとの間の変換を回避できることを知っていますが、ラムダ内のチェックされたIO例外を処理する必要があり、これは常に困難です。パフォーマンスが非常に重要であり、変換が受け入れられない場合は、ラムダ内のチェックされたIOExceptionをUncheckedIOExceptionとして伝達するか、Files APIを完全に無視してFileオブジェクトのみを処理します。

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


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

ここlistFilesで、ArrayList内のすべてのファイルのコレクションです。


1

グアバ注文を試すことができます:

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

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

私が同じ問題を探していたときにこの投稿に来ましたandroid。これが最終更新日でファイルを並べ替える最良の方法だとは言いませんが、私が見つけた最も簡単な方法です。

以下のコードは誰かに役立つかもしれません-

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
    }

ありがとう


しかし、誰が分類を行うのでしょうか?
DAB

初期化部分でforループあなたは私が撮影している見ることができlist.length-1件までi >=0単に逆の順序であなたを反復ました。
Hirdesh Vishwdewa 2016

0

追加のコンパレータなしで問題を処理する非常に簡単で便利な方法があります。変更された日付をファイル名とともに文字列にコード化し、それを並べ替えて、後で再び取り除くだけです。

固定長20の文字列を使用し、変更された日付(長い)をその中に入れ、先行ゼロで埋めます。次に、この文字列にファイル名を追加します。

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

ここで何が起こるか:

ファイル名1:C:\ data \ file1.html Last Modified:1532914451455 Last Modified 20 Digits:00000001532914451455

ファイル名1:C:\ data \ file2.html Last Modified:1532918086822 Last Modified 20 Digits:00000001532918086822

filnamesを次のように変換します。

ファイル名1:00000001532914451455C:\ data \ file1.html

ファイル名2:00000001532918086822C:\ data \ file2.html

その後、このリストを並べ替えることができます。

あなたがする必要があるのは後で20文字を再び取り除くことです(Java 8では、.replaceAll関数を使用して1行だけで配列全体を取り除くことができます)


-1

大きな数を処理しないので、さらに簡単になる可能性のある完全に異なる方法もあります。

すべてのファイル名とlastModifiedの日付を取得した後で配列全体を並べ替える代わりに、リストの正しい位置に取得した直後に、すべてのファイル名を挿入できます。

あなたはこのようにそれを行うことができます:

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

object2を位置2に追加すると、object3が位置3に移動します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.