【问题标题】:Fast sort by date of huge file ArrayList按大文件 ArrayList 的日期快速排序
【发布时间】:2014-06-13 23:01:29
【问题描述】:

我在 Java 中有一个包含大量文件(约 40.000 个文件)的 ArrayList。我需要按日期升序/降序对这些文件进行排序。目前,我使用一个简单的

Collections.sort(fileList, new FileDateComparator());

FileDateComparator 在哪里

public class FileDateComparator implements Comparator<File>
{
       @Override
       public int compare(File o1, File o2)
       {
           if(o1.lastModified() < o2.lastModified())
               return -1;
           if(o1.lastModified()==o2.lastModified())
               return 0;
          return 1;
       }
    }

排序占用了我很长的时间,比如 20 秒或更多。有没有更有效的方法来实现这一点?我已经尝试过将 Apache I/O LastModifiedFileComparator 作为比较器,但它的实现方式似乎相同,因为它需要相同的时间。

【问题讨论】:

标签: java sorting arraylist


【解决方案1】:

我认为您需要缓存修改时间以加快速度。你可以例如试试这样的:

class DatedFile {
  File f;
  long moddate;

  public DatedFile(File f, long moddate) {
    this.f = f;
    this.moddate = moddate;
  }
};


ArrayList<DatedFile> datedFiles = new ArrayList<DatedFile>();
for (File f: fileList) {
  datedFiles.add(new DatedFile(f, f.lastModified()));
}
Collections.sort(fileList, new FileDateComparator());
ArrayList<File> sortedFiles = new ArrayList<File>();
for (DatedFile f: datedFiles) {
  sortedFiles.add(f.f);
}

(使用适当的 FileDateComparator 实现)

【讨论】:

  • 这会带来好处,但操作系统本身会缓存文件修改时间,因此可能不会有那么显着的改进。
  • @Raedwald 系统调用非常昂贵,所以我预计会有相对较大的改进。另外,我认为OP很可能通过列出目录来获取文件列表,至少在linux上意味着操作系统已经缓存了修改时间。
【解决方案2】:

排序是 O(n lg N),因此您的 40,000 个文件列表将需要大约 600,000 次操作(比较)。如果需要大约 20 秒,即每秒大约 30,000 次比较。因此,每次比较大约需要 100,000 个时钟周期。这不可能是由于受 CPU 限制的处理。排序几乎可以肯定是 I/O 限制而不是 CPU 限制。磁盘寻道特别昂贵。

您可以通过使用多线程来减少磁盘寻道的影响,从而缩短时间。也就是说,通过让多个读取排队并等待磁盘驱动器提供它们的数据。为此,请使用将文件名映射到修改时间的(并发)映射,并使用多个线程填充该映射。然后让您的排序方法使用该映射而不是使用 File.lastModified() 本身。

即使您只使用一个线程填充该映射,您也会获得一点好处,因为您的排序方法将使用本地缓存的修改时间,而不是每次都查询 O/S 以获取修改时间。这种缓存的好处可能不大,因为操作系统本身可能会缓存这些信息。

【讨论】:

    【解决方案3】:

    Java 的数组 .sort() (大约从 Java 6 开始)实际上是 TimSort [http://svn.python.org/projects/python/trunk/Objects/listsort.txt],最快的通用 #sort (在许多情况下比 qsort 好得多); 如果没有启发式算法,您将无法对任何东西进行明显更快的排序。

    “像 20 秒或更多”对我来说意味着您的问题可能是著名的 ApplicationProfilingSkippedByDeveloperException - 进行分析并找到确切的瓶颈。我会使用 OS 文件 I/O 作为一个;批量执行文件属性的本机请求,缓存结果然后立即处理它们似乎是这里唯一明智的解决方案。

    【讨论】:

      【解决方案4】:

      您需要缓存 lastModified() 一种方法是在比较器本身中。

      public class FileDateComparator implements Comparator<File> {
         Map<File, Long> lastModifiedMap = new HashMap<>();
      
         Long lastModified(File f) {
             Long ts = lastModifiedMap.get(f);
             if (ts == null)
                 lastModifiedMap.put(f, ts = f.lastModified());
             return ts;
         }
      
         @Override
         public int compare(File f1, File f2) {
             return lastModified(f1).compareTo(lastModified(f2));
         }
      }
      

      这将通过仅查找每个文件的修改日期一次来提高性能。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-07-27
        • 1970-01-01
        • 2015-11-19
        • 2012-09-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多