乐于分享
好东西不私藏

Java源码中的排序算法(七)-TimSort之Galloping 模式

Java源码中的排序算法(七)-TimSort之Galloping 模式

之前已经分享了3篇关于TimSort的内容,今天继续

Java源码中的排序算法(四)-TimSort

Java源码中的排序算法(五)-TimSort之扩展

Java源码中的排序算法(六)-TimSort之天然存在的run

你有没有想过,为什么 Collections.sort() 在处理“几乎有序”的数据时快得惊人?
答案藏在 Timsort 的一个精妙优化中——Galloping 模式


一、什么是 Galloping 模式?

Galloping模式命名简单粗暴,直抒胸臆,gallop本身就是飞奔的意思。是 Timsort 在归并两个有序片段(run)时,用于加速“一边倒”场景的智能策略。

想象合并两列已排序的队伍:

  • A 队:[1, 3, 5, 7, 9, ..., 99]
  • B 队:[100, 102, 104, ...]

普通归并会逐个比较 50 次,而 Galloping 能一眼看出“A 全小于 B”,直接批量移动——从 O(n) 比较降到 O(log n)


二、核心功能:批量跳过,减少比较

目标

当一个 run 的多个连续元素都小于(或大于)另一个 run 的当前元素时,快速定位可批量移动的边界,避免逐个比较。

触发条件

  • 连续 MIN_GALLOP(默认 7)次“一边倒”比较
    (例如:A 的元素连续 7 次 < B 的头部)

三、关键逻辑 + JDK 21 源码示例

Galloping 分两步走,我们结合 JDK 21 TimSort.java 源码来看:

步骤 1:指数跳跃(找大致范围)

// TimSort.java - gallopRight 方法片段
int lastOfs = 0;
int ofs = 1;
while (ofs < len && c.compare(a[base + ofs], key) < 0) {
    lastOfs = ofs;
    ofs = (ofs << 1) + 1// 指数增长: 1 → 3 → 7 → 15...
if (ofs <= 0) ofs = len; // 防整数溢出
}
if (ofs > len) ofs = len;

作用:快速跳过连续小于 key 的区域,找到上界 ofs 和下界 lastOfs


步骤 2:二分查找(精确定位)

// 在 [lastOfs, ofs) 区间内二分查找第一个 >= key 的位置
int result = lastOfs + binarySearch(a, base + lastOfs, base + ofs, key, c);

其中 binarySearch 是标准二分查找实现。

总比较次数 ≈ 2·log₂(k)(k 为实际可移动长度)
普通归并需 k 次比较

例:k=1000 → Galloping 约 20 次 vs 普通 1000 次!


步骤 3:动态调整阈值(越用越聪明)

// mergeLo/mergeHi 中的自适应逻辑
if (count1 >= MIN_GALLOP || count2 >= MIN_GALLOP) {
// 成功找到长序列 → 降低下次触发门槛
    minGallop = Math.max(MIN_GALLOP, minGallop - 1);
else {
// 失败 → 提高门槛,避免无效切换
    minGallop++;
}

智能反馈:让 Galloping 只在真正有效时启用。


四、对 Timsort 的作用:自适应性能的关键

场景
普通归并
启用 Galloping
完全随机数据
正常工作
几乎不触发(无开销)
尾部随机(+sort)
大量比较
减少 30%~50% 比较
多段有序日志
逐个比
减少 40%~60% 比较
极端一边倒
O(n) 比较
O(log n) 比较

核心价值:在真实业务数据(如时间序列、ID 递增)上,显著提升性能,而对随机数据零惩罚。


五、与其他方法对比

方法
原理
优点
缺点
适用场景
普通归并
逐个比较
简单稳定
一边倒时效率低
通用
纯二分归并
每次用二分找插入点
最坏 O(n log n)
小数据常数大
理论研究
Galloping
指数跳 + 二分
自适应

:只在有效时启用
实现稍复杂
真实数据

Galloping 的优势

  • 不是“永远用高级方法”,而是“该用时才用
  • 动态调整阈值(minGallop),越用越聪明

六、总结

Galloping 模式是 Timsort 工程智慧的缩影

  • 尊重现实:真实数据常有“一边倒”结构;
  • 智能切换:仅在收益 > 成本时启用;
  • 高效定位:指数跳跃 + 二分,兼顾速度与精度。

下次当你调用 list.sort() 时,不妨想想:背后可能正有一匹“骏马”在数据草原上飞奔,为你省下成千上万次无谓的比较。


💡 感谢你看完这篇内容,这是我自己在工作学习中遇到的case,做一些简单的 究,并总结经验,如有遗漏或不合理的地方,欢迎你提出问题,让我们一起探索

本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » Java源码中的排序算法(七)-TimSort之Galloping 模式

评论 抢沙发

9 + 8 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮