为啥合并排序用于 Android/Java API 中的对象?

Posted

技术标签:

【中文标题】为啥合并排序用于 Android/Java API 中的对象?【英文标题】:Why Merge sort is used for objects in Android/Java API?为什么合并排序用于 Android/Java API 中的对象? 【发布时间】:2015-05-02 09:31:36 【问题描述】:

在 Java Arrays.sort() 中,原始类型使用快速排序。另一方面,Arrays.sort() 对象使用合并排序。而且,Collection.sort() 也是如此,它也使用合并排序。集合排序在下面使用数组排序实现。所以,简单来说,我可以说基元是使用快速排序来排序的,而对象是使用合并排序来排序的。

我的猜测是它与自身的排序算法有关。关于快速排序与合并排序的 SO 讨论很多,例如 this 和 this。似乎对于哪个更好,这是可以理解的,因为这取决于数据集。

我的理解是

就地:快速排序获胜。可以为链表就地实现合并排序 外部存储数据:合并排序获胜。 排序列表(由任何形式的链表支持):合并排序获胜。 Link

android API 似乎遵循与 Java 相同的模式。这是我在Arrays.java找到的

    public static void sort(long[] array) 
    DualPivotQuicksort.sort(array);

还有这个,

public static void sort(Object[] array) 
    ComparableTimSort.sort(array);

我不明白的是,是什么让 Merge 排序成为在 Java 或 Android 中排序对象的好候选?为什么不把这个决定留给开发人员呢?

【问题讨论】:

【参考方案1】:

关键问题是排序稳定性 - 如果从排序顺序的角度来看两个元素相等,它们在结果中出现的顺序是否与输入中的顺序相同。

没关系,例如long。输入中3 的所有实例将被组合在一起,没有人关心哪个是哪个。

另一方面,对象可能在不影响排序顺序的方式上有所不同。如果您按腿数对动物进行分类,您可能会关心“猫”和“狗”是否保持原来的顺序。

Arrays.sort 合并排序是稳定的。用于基元的快速排序不需要是稳定的。

【讨论】:

很好地解释了稳定排序的好处。【参考方案2】:

请看这个答案:Why Collections.sort uses merge sort instead of quicksort?

TL;DR:

与归并排序相比,QuickSort 有两个主要缺陷:

    不稳定(如 parsifal 所述)。 不保证 n log n 性能;它可能会在病理输入上降级为二次性能。

【讨论】:

【参考方案3】:

我觉得问题

是什么使归并排序成为对 Java 对象进行排序和 对原语进行快速排序?

已经有人回答了,但是

的问题

为什么这个决定没有留给开发人员?

尚未解决。

事实上,任何开发人员都可以轻松地从 CollectionArrayList 派生新类,并隐藏 sort() 方法(因为 sort()static 方法必须是 hidden, not overidden) 并使用他们自己的自定义实现。

很少(如果有的话)这样做的事实可能是因为当今大多数年轻程序员对 Java 的接触比对 C++ 的接触要多。在 Java 社区中,“抽象”意味着你不知道类的底层实现是如何工作的,而且你不需要知道。 JVM 的机器独立性影响了我们对速度/效率权衡的直觉。因此,我们中的许多人对数据结构和算法的理解不如一些年长且经验丰富的程序员那么清楚。

这几乎与 C++ 中对抽象的理解相反。引用 Alex Stepanov 的话:

事实上,我不相信图书馆可以消除 程序员需要了解算法和数据结构。它 仅消除了程序员实现它们的需要。一需要 了解要使用的数据结构的基本属性 它们正确地使应用程序满足其自身的复杂性 要求。

【讨论】:

【参考方案4】:

归并排序是稳定的:相同的元素不会因为排序而重新排序

【讨论】:

以上是关于为啥合并排序用于 Android/Java API 中的对象?的主要内容,如果未能解决你的问题,请参考以下文章

为啥查询优化器在合并连接后使用排序?

为啥逻辑适用于函数但不使用类,合并排序算法

为啥我的合并排序不像 O (n * lg n))?

算法导论中,为啥合并排序的递归树的高度为lgn?

我可以将 graphql API 数组合并/组合成一个吗?

为啥不在 Stream API 中使用 reduce 来合并多个映射?