arrays.binarysearch性能非固定o(log n),小数组(jdk8阈值为21)采用线性扫描以利用cpu缓存与分支预测优势,大数组才启用防溢出的二分查找,中点计算用low + (high - low) / 2避免整数溢出。

Arrays.binarySearch 的性能不是固定不变的“O(log n)”,它会根据数组长度自动切换策略:小数组走线性扫描,大数组才启用二分查找。这种设计兼顾了现代 CPU 缓存与分支预测特性,实际表现比教科书公式更贴近真实场景。
小数组用线性扫描,不是 bug 是优化
当数组长度小于阈值(JDK 8 中为 21),binarySearch 直接遍历,不计算中点、不递归跳转。这不是偷懒,而是因为:
- CPU 对短距离连续内存访问有极佳缓存命中率
- 避免除法和边界判断带来的分支开销
- 对 10 个元素的数组,线性扫描平均只需 5 次比较,比二分的至少 4 层逻辑更轻量
大数组启用安全二分,防溢出是关键细节
真正走二分路径时,中点计算采用 low + (high - low) / 2 而非 (low + high) / 2,这是为了防止索引相加溢出。例如在超大数组(如长度接近 Integer.MAX_VALUE)中,low 和 high 都可能很大,直接相加会变成负数,导致 mid 错误偏移。
同时循环条件为 low ,确保单元素区间(low == high)也能被检查,不会漏判。
返回值不只是“找没找到”,还编码插入位置
未命中时返回 -(insertion point + 1),这个设计让调用方能一步获得应插入位置:
- 若返回 -3,说明 key 应插在索引 2(因为 ~(-3 - 1) = 2)
- 若返回 -1,说明 key 小于所有元素,应插在开头
- 若返回 -(length + 1),说明 key 大于所有元素,应插在末尾
这个信息对维护有序列表、实现 TreeSet 或自定义有序容器非常实用。
排序前提不校验,错误静默且不可预测
binarySearch 完全不检查数组是否有序——传入乱序数组不会报错,但结果无定义:
- 可能返回错误下标(比如找到某个值,但它并非第一次出现的位置)
- 可能返回负数,但该负数不代表真实插入点(前提已破坏,计算失去意义)
- 逆序数组、部分有序数组、JSON 解析后未重排的字符串数组,都属于高危场景
务必在调用前确认已执行 Arrays.sort()(或按 Comparator 显式排序),尤其注意对象数组需实现 Comparable 或传入 Comparator。











