"Arrays.sort 对象数组是稳定排序,基本类型数组不是"是常见知识点,但"稳定"到底体现在哪、能不能真的拿来做二次排序,真实跑一遍最直观。
上一篇:finally 里的 return 真实会吞掉异常:真实复现一个安静的 bug
先按一个无关紧要的顺序(姓名字母序)插入数据,只按另一个字段(分数)排序,如果排序算法稳定,相同分数的人应该还保持插入时的字母序。
SortStability.java(测试数据) Student[] students = { new Student("Alice", 90), new Student("Bob", 85), new Student("Carol", 90), new Student("Dave", 85), new Student("Eve", 90), new Student("Frank", 85), }; Arrays.sort(students, Comparator.comparingInt((Student s) -> s.score).reversed());
排序时的 Comparator 只比较 score 这一个字段,完全没有提到姓名——如果姓名顺序还能保留下来,只能是排序算法本身稳定的结果。
原始顺序(按姓名字母序插入) = [Alice:90, Bob:85, Carol:90, Dave:85, Eve:90, Frank:85] 只按 score 降序排序后 = [Alice:90, Carol:90, Eve:90, Bob:85, Dave:85, Frank:85] score=90 组内真实顺序 = Alice Carol Eve (插入时是 Alice Carol Eve,如果稳定应该保持这个顺序) score=85 组内真实顺序 = Bob Dave Frank (插入时是 Bob Dave Frank,如果稳定应该保持这个顺序)两组的内部顺序都真实和插入顺序完全一致,证明 Arrays.sort(Object[]) 底层的 TimSort 真实是稳定排序
这个特性在实际场景里很有用:想按"分数降序,分数相同的按姓名升序"这种多关键字排序,不需要一次性写一个比较两个字段的复杂 Comparator——只要先按姓名把数据排好(或者本来数据源就是按姓名排的),再单独按分数排一次,稳定排序会自动帮你把姓名顺序保留在相同分数的组内。
"稳定性"这个概念本身要求元素带有除排序键之外的额外身份信息——原始类型数组恰恰没有这种信息。
排序前的分数: [90, 85, 90, 85, 90, 85] 排序后 = [85, 85, 85, 90, 90, 90]3 个 90 和 3 个 85,但没人能说出"哪个 90 是 Alice 的、哪个是 Carol 的",因为 int[] 里从头到尾就没存过这个信息
每一行都是真实运行这段代码得到的结果。