最高效方式取决于数组类型和数据范围:HashMap一次遍历统计(O(n)时间)适用于任意类型;整数范围有限时用索引数组最快;Stream API简洁但稍慢。

最高效的方式取决于数组类型和数据范围,核心是避免重复遍历、减少对象开销。
用 HashMap 一次遍历统计(通用推荐)
适用于任意类型(int、String、自定义对象等),时间复杂度 O(n),空间复杂度取决于唯一元素个数。
- 遍历时用 getOrDefault(key, 0) + 1 直接更新计数,无需先判空
- 对 int 数组,Integer 自动装箱无须额外处理;String 数组可直接作 key
- 自定义类作元素时,必须正确重写 equals() 和 hashCode(),否则每个实例都被视为不同 key
示例:
int[] arr = {1, 2, 2, 3, 3, 3};
Map<Integer, Integer> freq = new HashMap<>();
for (int num : arr) {
freq.put(num, freq.getOrDefault(num, 0) + 1);
}
立即学习“Java免费学习笔记(深入)”;
用索引数组替代 HashMap(整数范围有限时最快)
当元素都是非负整数,且最大值可控(如 0–1000),用普通数组下标计数,跳过哈希计算,内存与速度双优。
- 声明长度为 maxValue + 1 的 int 数组(如统计 0~99,就 new int[100])
- 遍历时以元素值为下标:count[num]++
- 注意:不支持负数或过大值,否则抛 ArrayIndexOutOfBoundsException 或浪费大量空间
示例:
int[] digits = {1, 3, 3, 7, 1, 3};
int[] count = new int[10]; // 覆盖 0~9
for (int d : digits) count[d]++;
用 Stream API 简洁写法(适合小数据或强调可读性)
代码更短、函数式风格清晰,但会创建中间对象,大数据量时略慢于手动循环。
- 基本类型数组需先转为包装类型流:Arrays.stream(arr).boxed()
- 用 Collectors.groupingBy(Function.identity(), Collectors.counting())
- 返回 Map<T, Long>,若需 Integer 计数,可用 Collectors.summingInt(e -> 1)
示例(String 数组):
String[] words = {"a", "b", "a"};
Map<String, Long> freq = Arrays.stream(words)
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
高频场景优化技巧
如果后续要查多个元素频次,或做 Top-K 分析,建议一步到位:
- 用 merge() 替代 getOrDefault:map.merge(num, 1, Integer::sum),语义更明确
- 找出现最多元素?遍历 map entrySet 时维护 max 值,比先排序再取更快
- 需按频次排序?把 entrySet 转 List 后用 Collections.sort() 或 stream().sorted(),主键 compare by value,次键可加原始值或首次索引


















