【问题标题】:Java Arrays.sort() Taking a Long TimeJava Arrays.sort() 需要很长时间
【发布时间】:2014-04-29 02:49:15
【问题描述】:

我正在使用 Java 的 Arrays.sort() 函数按文件的最后修改时间对文件列表进行排序。 245 个文件的排序大约需要 5 秒。这对我来说似乎太长了。我觉得不应该超过0.5秒。这是一个很好的假设吗?我究竟做错了什么?或者这听起来正常吗?

public static class LastModifiedComparator implements Comparator<File> {
    @Override
    public int compare(File f1, File f2) {
        return (int)(f1.lastModified() - f2.lastModified());
    }       
}

File folder = new File( "C:\\Whatever\\" );
File[] filesInFolder = folder.listFiles();
logger.debug("Starting File Sort");
Arrays.sort(filesInFolder, new LastModifiedComparator());
logger.debug("Done File Sort");

日志输出

2012-08-10 14:24:20,333 DEBUG http-8080-4 <ClassName>:73 - Starting File Sort
2012-08-10 14:24:25,915 DEBUG http-8080-4 <ClassName>:75 - Done File Sort

【问题讨论】:

  • 如果文件是唯一的(都有不同的 equals 方法),使用 TreeSet 看看会发生什么会很有趣。您可能必须将文件包装在另一个实现比较器的类中。不过 5 秒似乎确实很长!
  • 文件夹中有多少个文件?
  • 您也尝试过将它作为一个集合并使用 ArrayList 和 Collections.sort()。有什么不同吗?
  • 您没有告诉我们您要排序的数组有多大。很可能数组非常很大。

标签: java sorting


【解决方案1】:

您需要改进您的Comparator 逻辑。您需要缓存 lastModified() 值,因为该方法的实现速度很慢。我建议将 File 实例包装到您制作的 comparable 对象中,该对象将缓存该值:

public class FileLmWrapper implements Comparable<FileLmWrapper> {
  public final File f;
  public final long lastModified;
  public FileLmWrapper(File f) { 
    this.f = f; 
    lastModified = f.lastModified();
  }
  public int compareTo(FileLmWrapper other) {
    return Long.compare(this.lastModified, other.lastModified);
  }
}

【讨论】:

  • 谢谢,包装器的作用就像一个魅力!排序现在以毫秒为单位。但是,整个操作仍然需要大约 1 秒。我猜构建这 245 个FileLmWrapper 对象需要时间。也许我应该尝试将时间放入 HashMap 而不是使用包装器!
  • @Danish 245 对象分配几乎不需要任何时间。如果您查看我在下面的帖子,我实际上通过推断每次调用 lastModified() 需要多长时间来预测您大约需要 1 秒。
  • 是的,您是正确的lastModified(),它将定义此操作的运行时间的下限。
  • @Danish 我仍然不知道为什么需要一秒钟。在我不是特别强大的笔记本电脑上,对lastModified() 的 245 次调用花费了大约 5.5 毫秒。
  • @yshavit 可能是因为该文件夹位于网络驱动器上。这是我在问题中没有提及的一条信息。
【解决方案2】:

File.lastModified 必须去操作系统查询文件最后一次修改的时间——它没有被缓存。每次比较你都会这样做两次,Arrays.sort 使用mergesort -- O(n log n)。为n 插入 245,大约是 580 次比较,或 1100 次操作系统调用以获得最后修改时间。这意味着您每秒可以获得大约 230 个最后修改的调用。这看起来可能有点慢,但肯定比在 JVM 中进行这么长时间的比较更合理

正如 Marko Topolnik abd NgSan 在上面指出的那样,解决方法是首先缓存所有文件的最后修改时间。我会通过创建一个新的类对象来组合文件和那个时间,然后对这些对象进行排序。这样一来,您将只有 245 次调用 File.lastModified,而排序时间应该是 1/5 左右。

【讨论】:

  • 这里你假设所有O(n log n) 都源于比较。真的是这样吗?还涉及复制和合并。
  • 我认为大部分是,是的。我做了一个快速的小测试来显示它:pastebin.com/Z7DNtiF1警告: 会向你的工作目录发送大量空文件!)
  • @MarkoTopolnik 只是为了完善它:当我在我的机器上运行该程序时,整数有 1721 次比较,耗时 2.1 毫秒。这些文件有 244 次比较(它们都具有相同的 lastModified 时间,因为它们创建得如此之快,所以排序只是 O(n))并且仍然花费了 5.5 毫秒。如果这些文件是按修改日期随机排序的,大约需要 39.0 毫秒(仍然比 OP 快得多!我不知道他的磁盘出了什么问题)。
  • 是的,这样会更有意义。
【解决方案3】:

我不确定,但听起来每次读取修改时间时它都在进行磁盘 I/O,因此速度很慢。简单地获取对象中的修改时间以及 File 对象,然后然后排序可能会更快。

【讨论】:

    【解决方案4】:

    您的比较操作

    @Override
    public int compare(File f1, File f2) {
        return (int)(f1.lastModified() - f2.lastModified());
    }  
    

    不仅仅是一个 getter,而是发出一个调用以从文件系统中获取信息,因此排序的高响应时间也是由于 lastModified() 的性能而不是 compare()

    【讨论】:

      【解决方案5】:

      modified Quick Sort 中在java 中实现的排序调整了合并排序,其平均运行时间复杂度为O(nlogn)。
      因此,我们需要专注于您的文件操作,例如获取 lastModifiedTime。您确定这些文件是需要网络延迟的本地文件或共享驱动器吗?

      【讨论】:

      • 其实是归并排序,它比快速排序做的比较少得多,代价是更多的内存。
      • 感谢您的指正,您是对的。实际上只对原始类型进行调整快速排序,但在对象的情况下,实现了合并排序。
      猜你喜欢
      • 2014-03-21
      • 2013-09-07
      • 2020-08-26
      • 2014-10-09
      • 2012-11-26
      • 2019-12-27
      • 2017-10-22
      • 2020-11-15
      • 2016-05-19
      相关资源
      最近更新 更多