5

我有一个文件列表,我想对其进行排序并提取最后修改的前 3 个文件。

约束:由于下游应用程序的兼容性问题,我无法使用 Java 7

我目前的选择

解决方案 1

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

解决方案 2

public static void sortFilesDesc(File[] files) {
  Arrays.sort(files, new Comparator() {
    public int compare(Object o1, Object o2) {
      if ((File)o1).lastModified().compareTo((File)o2).lastModified()) {
        return -1;
      } else if (((File) o1).lastModified() < ((File) o2).lastModified()) {
        return +1;
      } else {
        return 0;
      }
    }
  });
}

问题

上述两种解决方案需要更多时间来执行和内存。我的文件列表包含大约 300 个 tar 文件,每个文件大小为 200MB。所以它消耗更多的时间和内存。

有什么方法可以有效地处理这个问题吗?

每个比较操作都使用一个具有高内存的文件对象,有没有办法释放内存并有效地处理它?

4

4 回答 4

5

你可以做得更快。

Arrays.sort(...) 使用“快速排序”,它需要~ n * ln(n)操作。

此示例仅对整个数组进行一次迭代,即~ n次操作。

public static void sortFilesDesc(File[] files) {        
    File firstMostRecent = null;
    File secondMostRecent = null;
    File thirdMostRecent = null;
    for (File file : files) {
        if ((firstMostRecent == null)
                || (firstMostRecent.lastModified() < file.lastModified())) {
            thirdMostRecent = secondMostRecent;
            secondMostRecent = firstMostRecent;             
            firstMostRecent = file;
        } else if ((secondMostRecent == null)
                || (secondMostRecent.lastModified() < file.lastModified())) {
            thirdMostRecent = secondMostRecent;
            secondMostRecent = file;
        } else if ((thirdMostRecent == null)
                || (thirdMostRecent.lastModified() < file.lastModified())) {
            thirdMostRecent = file;
        }
    }
} 

在少量文件上,您不会看到太大的差异,但即使对于数十个文件,差异也会很大,对于更大的数字 - 戏剧性。

检查算法的代码(请输入正确的文件结构):

package com.hk.basicjava.clasload.tests2;

import java.io.File;
import java.util.Date;


class MyFile extends File {

    private long time = 0; 

    public MyFile(String name, long timeMills) {
        super(name);
        time = timeMills;
    }
    @Override
    public long lastModified() {
        return time;
    }
}

public class Files {

    /**
     * @param args
     */
    public static void main(String[] args) {

        File[] files = new File[5]; 
        files[0] = new MyFile("File1", new Date(2013,1,15, 7,0).getTime());
        files[1] = new MyFile("File2", new Date(2013,1,15, 7,40).getTime());
        files[2] = new MyFile("File3", new Date(2013,1,15, 5,0).getTime());
        files[3] = new MyFile("File4", new Date(2013,1,15, 10,0).getTime());
        files[4] = new MyFile("File5", new Date(2013,1,15, 4,0).getTime());
        sortFilesDesc(files);
    }

    public static void sortFilesDesc(File[] files) {        
        File firstMostRecent = null;
        File secondMostRecent = null;
        File thirdMostRecent = null;
        for (File file : files) {
            if ((firstMostRecent == null)
                    || (firstMostRecent.lastModified() < file.lastModified())) {
                thirdMostRecent = secondMostRecent;
                secondMostRecent = firstMostRecent;             
                firstMostRecent = file;
            } else if ((secondMostRecent == null)
                    || (secondMostRecent.lastModified() < file.lastModified())) {
                thirdMostRecent = secondMostRecent;
                secondMostRecent = file;
            } else if ((thirdMostRecent == null)
                    || (thirdMostRecent.lastModified() < file.lastModified())) {
                thirdMostRecent = file;
            }
        }
        System.out.println("firstMostRecent : " + firstMostRecent.getName());
        System.out.println("secondMostRecent : " + secondMostRecent.getName());
        System.out.println("thirdMostRecent : " + thirdMostRecent.getName());
    } 

}
于 2013-01-17T06:11:27.860 回答
3

你必须检查每个文件的 lastModified,你不能改变它。您不需要做的是对所有元素进行排序以获得前 3 名。如果您可以使用 Guava,则可以使用Ordering.greatestOf(它使用了一个很好的算法):

Ordering<File> ordering = Ordering.from( new Comparator(){
        public int compare(File f1, File f2)
        {
            return Long.valueOf(f1.lastModified()).compareTo(f2.lastModified());
        });

List<File> max3 = ordering.greatestOf(Arrays.asList(directory.listFiles()), 3);
于 2013-01-17T05:47:47.603 回答
0

您的问题是检索上次修改日期是一项相对昂贵的操作,因为它涉及操作系统逻辑。因此,如果您不介意获取最新的最新值,您可以将文件包装在可比较的类中。

public class LastModifiedFile implements Comparable<LastModifiedFile> {

    private final File file;
    private final Date lastModified;

    public LastModifiedFile(File file) {
        this.file = file;
        lastModified = file.lastModified();
    }

    public int compareTo(LastModifiedFile other) {
        return lastModified.compareTo(other.lastModified);
    }
}

请注意,在排序期间更改上次修改日期将导致许多排序算法出现未定义的行为。如果最后修改日期发生更改,Java 7s Tim Sort 实现将抛出异常,因此比较会导致不同的值。

于 2013-01-17T08:51:35.913 回答
0

我支持解决方案 1,但有一些改进

Arrays.sort(files, new Comparator<File>() {
        public int compare(File f1, File f2) {
            long d1 = f1.lastModified();
            long d2 = f2.lastModified();
            return d1 > d2 ? 1 : d1 < d2 ? -1 : 0;
        }
    });

避免由于 Long.valueOf(long) 而创建不必要的对象。

File不保存/读取任何文件数据,而只保存文件路径,没有性能/内存问题。这里唯一耗时的操作是从文件系统中读取修改时间,这是无法避免的。

于 2013-01-17T05:50:47.097 回答