array_count_values仅统计string和int类型的值频次,遇float/null/array/object会警告并跳过;需预先类型归一化,否则"1"与1被视为不同值;替代方案可用foreach手动处理以兼容复杂逻辑。

array_count_values 会丢键名,只认值类型
array_count_values 不处理关联键,只统计「值」本身出现次数,且要求所有值必须是 string 或 int。传入 float、null、array、object 会直接报 Warning 并跳过该元素:
Warning: array_count_values(): Can only count STRING and INTEGER values!
常见踩坑场景:
- 从数据库查出的字段含
null(比如可空的 status 字段),直接传给array_count_values会静默丢失 - 前端传来的 JSON 解析后,数字可能被当成
float(如1.0),和整数1被视为不同值 - 用
array_column($data, 'tag')提取一列时,若某行缺失该 key,对应值为null,也会被过滤掉
统计前必须做类型归一化
如果原始数组含混合类型(比如字符串数字 "1" 和整数 1),得先统一转换再统计,否则它们会被算作两个不同项:
$arr = ["1", 1, "2", 2]; print_r(array_count_values($arr)); // 输出:Array ( [1] => 1 [2] => 1 ) —— 只统计了 string 键?不对,其实是只保留了后出现的同值覆盖?不,实际是:string "1" 和 int 1 被视为不同值,但 <code>array_count_values</code> 内部做了类型敏感比较,所以结果是: // Array ( ["1"] => 1 [1] => 1 ["2"] => 1 [2] => 1 )?错——实测 PHP 8.1 下结果为: // Array ( ["1"] => 1 [1] => 1 ["2"] => 1 [2] => 1 ) // 但注意:key 是原值,类型保留;所以 "1" 和 1 确实被当两个 key
安全做法是提前 cast:
- 全转字符串:
array_map('strval', $arr) - 全转整型(适合纯数字):
array_map('intval', $arr),但注意"1.9"会变1 - 更稳妥的数字归一:
array_map(fn($v) => is_numeric($v) ? (int)$v : (string)$v, $arr)
替代方案:手动 foreach 更可控
当需要保留原始键逻辑、或要兼容非标类型时,别硬套 array_count_values。一个 4 行 foreach 往往更清晰:
$counts = [];
foreach ($arr as $v) {
// 可在此处做 null 过滤、类型转换、甚至正则 normalize
if ($v === null) continue;
$key = is_float($v) ? (int)$v : $v; // 示例:把 float 1.0 当 1 统计
$counts[$key] = ($counts[$key] ?? 0) + 1;
}
优势:
- 不报错,可自定义跳过/修正逻辑
- 能同时访问原数组的键(比如只统计某个 key 下的值)
- 配合
isset()或??避免 notice
性能差异其实没那么大,别过早优化
对万级以内数组,array_count_values 和手写 foreach 耗时差不到 0.1ms。PHP 内部实现是 C,确实快,但瓶颈通常不在这里:
- 真正慢的是前面的数据提取(比如
array_column+ 多维遍历) - 或者后续还要做排序(
arsort)、取 topN(array_slice) - 如果数组来自
json_decode,解析开销远大于统计本身
所以优先保证逻辑正确和可维护性——类型不干净就别图省事硬塞 array_count_values。
最常被忽略的一点:它不递归。想统计多维数组里所有叶子节点?得先 array_walk_recursive 扁平化,或者老实用递归函数。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











