array_count_values()是PHP唯一原生一步实现“值→频次”映射的函数,仅支持索引数组且元素须为字符串或整数;传入非法类型会触发Warning;使用前需array_values()归一化键名,混合类型(如'1'与1)被区分统计;二维数组须先扁平化;筛选高频元素需array_filter()+array_keys();保持原序重复结构需两次遍历并use频次表;大数据量时注意内存占用与重复调用开销。

用 array_count_values() 统计频次最直接
PHP 原生函数 array_count_values() 是唯一能一步完成「值 → 出现次数」映射的内置方法,它只接受索引数组(不能是关联数组),且元素必须是字符串或数字。如果传入对象、资源或 null,会直接报 Warning: array_count_values(): Can only count string and integer values。
实操建议:
- 先用
array_values()归一化键名,避免因关联键干扰统计 - 若原数组含混合类型(如
'1'和1),注意 PHP 会区分它们——'1'是字符串,1是整数,统计结果中算两个不同项 - 对二维数组需先扁平化,例如用
array_merge(...$arr)(PHP 5.6+)或call_user_func_array('array_merge', $arr)
筛选出现次数 ≥ N 的元素怎么写
拿到 array_count_values() 返回的频次数组后,用 array_filter() 配合匿名函数即可筛选。关键点在于:返回的是「值→次数」映射,不是原始数组,所以筛选后要再用 array_keys() 提取符合条件的值。
示例(保留至少出现 2 次的元素):
立即学习“PHP免费学习笔记(深入)”;
$arr = ['a', 'b', 'a', 'c', 'b', 'a'];
$counts = array_count_values($arr); // ['a'=>3, 'b'=>2, 'c'=>1]
$result = array_keys(array_filter($counts, function($freq) { return $freq >= 2; }));
// $result === ['a', 'b']
注意:array_filter() 默认保留键名,所以 array_keys() 才能拿到原始值;若误用 array_values(),会丢掉值本身,只剩次数。
想保留原始顺序和重复结构怎么办
上面方法只返回去重后的值列表。如果需求是「从原数组中提取所有出现 ≥N 次的元素,保持原有顺序和重复次数」,就不能只靠 array_count_values() 一次过。得两步走:
- 先统计全量频次,存到变量里
- 再遍历原数组,对每个元素查频次表,满足条件就保留
示例:
$arr = ['a', 'b', 'a', 'c', 'b', 'a'];
$counts = array_count_values($arr);
$result = array_filter($arr, function($v) use ($counts) {
return $counts[$v] >= 2;
});
// $result === ['a', 'b', 'a', 'b', 'a'](键名保留,可用 array_values() 重排索引)
这里容易漏掉 use ($counts),否则闭包内访问不到频次表,会触发 Undefined index 警告。
大数据量时性能要注意什么
array_count_values() 时间复杂度是 O(n),但内部有哈希表构建开销;两次遍历(统计 + 筛选)在万级元素下几乎无感,但若数组超 10 万且内存受限,频繁的 array_filter() 可能引发临时数组膨胀。
优化方向:
- 用
foreach手动单次遍历:边统计边缓存满足条件的值,避免二次扫描 - 若只需判断「是否存在高频项」,统计中途加
break,不必跑完全部 - 避免在循环中反复调用
array_count_values()—— 它不是轻量操作
真正卡顿的往往不是算法,而是没意识到 array_count_values() 会把原始数组整个载入内存做哈希映射。原始数组含大字符串或对象引用时,这点尤其明显。











