用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
“新长”部分是什么?您为什么不只是比较买单呢?那将避免您创建大量的long只是为了获得compareTo方法...
John Gardner

该代码无法编译。compare方法期望返回的是int而不是Long。
marcospereira

1
我是唯一认为这种解决方案疯狂的人吗?您正在打电话file.lastModified()很多次。最好先获取所有日期,然后再订购,因此file.lastModified()每个文件仅调用一次。
cprcrack

1
您可以使用apache commons比较器:Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_REVERSE);
jlunavtgrad

5
Java 8有更好的解决方案(请参见viniciussss答案):Arrays.sort(files, Comparator.comparingLong(File::lastModified));
破星

Answers:


99

我认为您的解决方案是唯一明智的方法。获取文件列表的唯一方法是使用File.listFiles(),文档说明这不能保证返回文件的顺序。因此,您需要编写一个使用File.lastModified()Comparator,并将其以及文件数组传递给Arrays.sort()


如何在此处修复格式?在预览中看起来不错,但第4个链接已拧紧。
Dan Dyer

1
在比较方法违规错误中对最终结果进行排序时,File.lastModified可能会更改,请参阅:stackoverflow.com/questions/20431031请参阅stackoverflow.com/a/4248059/314089,以获得可能的更好解决方案。
icyerasor

48

如果您有许多文件,这可能会更快。这使用了decorate-sort-undecorate模式,因此每个文件的最后修改日期仅被获取一次,而不是每次排序算法比较两个文件时都被获取。这潜在地将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

1
当您担心由于违反比较方法而没有得到IllegalArgumentException时,也应该使用此方法。如果有多个文件具有相同的lastModified值,则使用Map的方法将失败,这将导致忽略这些文件。这绝对应该是一个公认的答案。
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));
破星号'18

@starbroken如果文件是由directory.listFiles()返回的简单数组,如File [],则您的解决方案将不起作用。
viniciussss

@starbroken为了使您的解决方案有效,需要使用ArrayList<File> files = new ArrayList<File>(Arrays.asList(directory.listFiles())),这不仅比容易File[] files = directory.listFiles()
viniciussss

是的,我同意你的看法。如果您有文件数组,则无需创建列表。(如果有人想知道,ArrayList<File>(...)需要在viniciussss注释中添加“ additional” 以获取可以排序的可变列表。)我发现此线程正在寻找一种对文件列表进行排序的方法。因此,我刚刚添加了该代码,以便人们在碰巧也有列表的情况下可以简单地复制它。
破星

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+。
加博(Gábor)2014年

4
使用return Long.valueOf(f1.lastModified())。compareTo(f2.lastModified()); 而不是较低的api。
马丁·赛克斯

25

您可能还会看到apache commons IO,它具有一个内置的最后修改的比较器以及许多其他用于处理文件的实用工具。


5
此解决方案在javadoc中存在一个奇怪的错误,因为javadoc说要使用“ LastModifiedFileComparator.LASTMODIFIED_COMPARATOR.sort(list);”。对列表进行排序,但是LASTMODIFIED_COMPARATOR被声明为“ Comparator <File>”,因此它不公开任何“ sort”方法。
特里斯坦(Tristan)2012年

4
像这样使用它:链接
cleroo 2012年

1
在比较方法违规错误中对最终结果进行排序时,File.lastModified可能会更改,请参阅:stackoverflow.com/questions/20431031请参阅stackoverflow.com/a/4248059/314089,以获得可能的更好解决方案。
icyerasor

1
爱apache公地,节省了很多时间,
redDevil 2015年

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会有所帮助。
2015年

完成,感谢宽带
Balaji Boggaram Ramanarayan

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对象之间的转换,但是,那么您需要在lambda内部处理已检查的IO异常,这总是很麻烦的。我想说的是,如果性能足够关键,以至于转换是不可接受的,那么我要么将lambda中的已检查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());
        }
    });

listFilesArrayList中所有文件的集合在哪里


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
    }

谢谢


但是谁来分类呢?
民建联

for循环的初始化部分中,您可以看到我已经list.length-1习惯了i >=0以相反的顺序迭代您。
Hirdesh Vishwdewa '16

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上次修改时间:1532914451455上次修改时间20位:00000001532914451455

文件名1:C:\ data \ file2.html上次修改时间:1532918086822上次修改时间20位:00000001532918086822

将filname转换为:

文件名1:00000001532914451455C:\ data \ file1.html

文件名2:00000001532918086822C:\ data \ file2.html

然后,您可以对该列表进行排序。

您需要做的就是稍后再剥离20个字符(在Java 8中,您可以使用.replaceAll函数仅用一行来剥离整个Array)


-1

还有一种完全不同的方法,可能会更容易,因为我们不处理大量数据。

检索所有文件名和lastModified日期后,无需对整个数组进行排序,而只需在检索到文件名后将其插入列表的正确位置即可。

您可以这样做:

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

将对象2添加到位置2之后,它将对象3移动到位置3。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.