arrays.binarysearch不能查找多个匹配项,只返回任一匹配索引;需先定位再向左右线性扩展,或用两次二分查找找左右边界;频繁查询应改用map或treemultiset等结构。

Arrays.binarySearch 本身不支持查找多个匹配项,它只保证返回“其中一个”匹配元素的索引(若有),且不指定是第一个、最后一个还是中间任意一个。这是二分查找算法的固有特性——找到即停,不继续遍历。
为什么不能直接用 binarySearch 找全部重复项
二分查找依赖有序性做快速剪枝,一旦命中目标值就立即返回,不会向左或向右扫描相邻相等元素。即使数组中存在多个相同值(如 {1, 2, 2, 2, 3} 中查 2),binarySearch 可能返回索引 1、2 或 3 中的任意一个,结果不确定。
获取所有匹配索引的常用做法
在已排序数组中找全部重复项,核心思路是:先用 binarySearch 快速定位一个匹配点,再以此为中心向左右线性扩展。
- 调用
Arrays.binarySearch(arr, key)得到任一匹配索引mid - 若
mid ,说明无匹配,直接返回空集合 - 否则,从
mid开始:- 向左扫描:递减索引,直到
arr[i] != key或越界 - 向右扫描:递增索引,直到
arr[j] != key或越界
- 向左扫描:递减索引,直到
- 收集区间
[leftBound, rightBound]内所有索引
更高效:用 Arrays.binarySearch 查边界位置
避免线性扫描,可借助两次 binarySearch 分别找“左边界”和“右边界”:
- 左边界:查找
key的**第一个出现位置** → 调用Arrays.binarySearch(arr, key)不够,需自定义逻辑或使用Arrays.binarySearch配合虚拟值(如对int[]查key - 0.5不可行)→ 实际中推荐手写边界查找,或改用Collections.binarySearch配合List时结合indexOf/lastIndexOf - 更实用的做法:用标准库外的辅助方法,例如
示例(适用于 int[]):
public static int[] findAllIndices(int[] arr, int key) {
int pos = Arrays.binarySearch(arr, key);
if (pos 0 && arr[left - 1] == key) left--;
while (right left + Arrays.asList(arr).indexOf(i)) // 简化示意,实际应构造索引数组
.mapToInt(Integer::intValue)
.toArray();
}
更简洁可靠的方式是:生成索引范围 int[] indices = new int[right - left + 1],再用循环填入 left 到 right。
替代方案:改用其他数据结构
如果业务中频繁需要按值查多个索引,原始数组不是最优选择:
- 构建
Map<integer list>></integer>:键为值,值为所有出现位置的列表(适合静态数据预处理) - 使用
TreeMultiset(Guava)或TreeMap自定义计数+位置映射 - 对对象数组,可先用
Stream过滤:IntStream.range(0, arr.length).filter(i -> arr[i].equals(key)).toArray()(小数据量够用,大数据牺牲 O(n) 时间)











