【问题标题】:Finding the 3 most recently modified files in a long list of files在长长的文件列表中查找最近修改的 3 个文件
【发布时间】:2012-12-31 14:56:40
【问题描述】:

我有一个文件列表,我想对其进行排序并提取最后修改的前 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。所以它会消耗更多的时间和内存。

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

每个比较操作都使用一个高内存的文件对象,有什么办法可以释放内存并有效地处理这个问题?

【问题讨论】:

  • 我认为您的内存和时间问题不是由于您对 300 个项目的排序(无论如何都在内存中)。也许您不止一次执行排序?
  • 不,我正在使用上述两种解决方案中的任何一种。您所说的“无论如何都在内存中”是什么意思,一旦操作完成,我该如何清除。
  • File 对象不是昂贵的对象!它只包含文件名,不包含文件的内容。所以文件大小是完全无关的。

标签: java file sorting memory memory-management


【解决方案1】:

你可以做得更快。

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

}

【讨论】:

  • 请删除“几乎 6 次”的说法。您可能无法通过在复杂性公式中插入数字来比较不同算法的运行时间。结论是正确的——但论证是有缺陷的。
  • 我同意,这并不完全正确;那只是为了让人们“感受不同”。
  • 感谢@AlexKreutznaer。您的解决方案不适用于示例的所有案例场景,请考虑以下内容并针对您的算法进行跟踪。文件名:FXXXXXXXXX 修改时间 07:00 HRS 文件名:FXXXXXXXXX 修改时间 07:40 HRS 文件名:MXXXXXXXXX 修改时间 05:00 HRS 文件名:YXXXXXXXXX 修改时间 10:00 HRS 文件名:YXXXXXXXXX 修改时间 04:00 HRS
  • 为什么不起作用?它将完美地工作。你能解释为什么你认为它不起作用吗?您的示例中没有任何内容会使其不起作用。
  • 你跟踪算法了吗?你能给出你的追踪结果吗?我确定您的跟踪中有错误。任务很简单。
【解决方案2】:

你必须检查每个文件的 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);

【讨论】:

    【解决方案3】:

    我支持解决方案 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 不保存/读取任何文件数据,只保存文件路径,不存在性能/内存问题。这里唯一耗时的操作是从文件系统中读取修改时间,这是无法避免的。

    【讨论】:

    • Long.compare 出现在 Java 7 中,问题要求非 Java 7 解决方案。
    • 现在您已将代码更改为无法编译的代码(不兼容的类型,int/long)。
    • 整个问题是关于效率的,我的改进是关于摆脱创建 2 个 Long 对象。
    【解决方案4】:

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

    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 实现将抛出异常,因此比较会导致不同的值。

    【讨论】:

      猜你喜欢
      • 2011-06-16
      • 1970-01-01
      • 2018-09-28
      • 1970-01-01
      • 1970-01-01
      • 2013-04-28
      • 1970-01-01
      • 2012-06-12
      相关资源
      最近更新 更多