【问题标题】:Why does Java's sort implementation convert a list to an array before sorting?为什么Java的排序实现会在排序前将列表转换为数组?
【发布时间】:2015-07-29 04:59:18
【问题描述】:

在 JDK 1.8 中,java.util.List#sort(Comparator) 方法的第一条语句如下:

Object[] a = this.toArray();

将列表复制到数组中,对其进行排序,然后将列表的每个节点重置为数组中排序后的值,成本很高。

在对ArrayList 进行排序时,似乎可以不将值复制到临时数组中。我对吗?如果没有,是什么指导了该方法的创建者?

【问题讨论】:

  • 列表实现有很多,包括链表,其中对列表本身进行排序可能成本更高。
  • "在 ArrayList 的情况下,似乎可以不将值复制到数组。" - 这正是它的完成方式。
  • 感谢您的快速回复。

标签: java arrays list sorting java-8


【解决方案1】:

java.util.List 接口中的sort 只是列表排序的默认 实现。

ArrayList 使用排序方法覆盖此默认值,该方法直接对其内部数组进行排序。

【讨论】:

    【解决方案2】:

    对于 ArrayList,或者其他随机访问的 List,你可能是对的。

    但是,Collections.sort 支持任何 List 实现。例如,对于 LinkedList,在排序期间交换元素会非常广泛(因为查找第 i 个元素需要线性时间)。

    将List转为数组,并在数组排序后设置原始List的元素,给算法增加了一个线性时间分量,不会改变(O(nlog(n)))的渐近运行时间。

    【讨论】:

    • 看起来像这样的简单代码: if(this instanceof ArrayList) { // only sort } else { // do stuff as平常 } 将改进方法。我说的对吗?
    • @the_kaba 这不是必需的:正如 greg 指出的那样,ArrayList 之类的实现将覆盖此默认方法,因此它们无需先创建数组即可执行排序。
    【解决方案3】:

    在 OpenJDK 1.8 中,java.util.ArrayList#sort(Comparator) 不会复制其内部数组;它按照您的建议排序。

    您批评的java.util.List#sort() 的实现具有以下随附文档:

    默认实现获取包含此列表中所有元素的数组,对数组进行排序,并遍历此列表,从数组中的相应位置重置每个元素。 (这避免了 n² log(n) 性能,该性能会因尝试对链表进行就地排序而导致。)

    也就是说,复制数组并随机访问比在链表中线性移动的开销更有效。常见的实现试图用复制开销换取元素访问开销。对于像ArrayList 这样不需要复制来获得对元素的相同随机访问的情况,库通过覆盖该方法来省略复制。

    一个有趣的比较:看看 C++ 标准库的 std::sort()std::list::sort() 函数:

    通用std::sort() 算法更有效,但该库禁止在std::list(这是一个链表,类似于Java 的java.util.LinkedList)上调用它。为了方便起见,该库提供了一种效率较低的方法来对std::list 进行排序。与 Java 库不同,C++ 库不会为了使用随机访问 std::sort() 算法而将 std::list() 复制到数组中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-11
      相关资源
      最近更新 更多