ascii字符统计用char作数组下标(int[128])高效省空间,但超出0–127范围会越界;中文、emoji等unicode字符须用string.codepoints()配合map或大数组,避免charat()导致的代理对错误。

用 char 数组做索引计数,只适用于 ASCII 字符
直接用 char 当数组下标,本质是利用其整数值(0–127)做桶编号。这方法快、内存省、无对象开销,但前提是字符全在 ASCII 范围内——比如统计英文文本、数字、符号没问题;一旦出现中文、emoji 或其他 Unicode 字符(如 '\u4F60'),char 值可能超 127,数组越界或覆盖错位。
- 声明长度为 128 的
int[] count = new int[128]就够了 - 遍历字符串:对每个
c,执行count[c]++(c自动转为int) - 查某个字符频次:直接
count['a'],O(1);查所有结果,遍历 128 项即可 - 注意:
char是无符号 16 位,但作为数组下标时会被截断为 int,负值不会出现;真正风险是超出 127 后写进错误位置
遇到中文或扩展字符必须换方案
Java 的 char 是 UTF-16 单元,一个汉字(如 '你')对应一个 char,值约 20320,远超 128;而 emoji 如 '??' 还涉及代理对(surrogate pair),单个 char 根本无法表示。这时候硬用数组会越界或漏统计。
- 改用
Map<character integer></character>:安全通用,自动处理任意char和代理对(只要用String.codePoints()遍历) - 若只处理 BMP 字符(含常见中文),可用
int[65536],但内存占用升到 256KB,且仍不支持增补字符(如某些 emoji) - 更稳的做法是用
String.codePoints().forEach(cp -> map.merge(cp, 1, Integer::sum)),cp是完整 Unicode 码点(int类型)
String.charAt(i) 和 String.codePoints() 别混用
这是最容易出统计偏差的地方。用 charAt() 遍历字符串,在遇到代理对时会把一个 emoji 拆成两个 char,导致多计一次、字符意义丢失;而 codePoints() 返回的是真正的 Unicode 码点流。
- 错误示范:
for (int i = 0; i —— 对 <code>"??"会执行两次,且两次的c都不是有效字符 - 正确做法:
s.codePoints().forEach(cp -> { /* 处理 cp */ });,其中cp是完整的码点值(如 128187) - 如果坚持用数组,得基于码点建桶:最大常用码点约 10 万,
int[120000]可覆盖大部分场景,但仍是权衡——空间换安全
性能差异真有那么大吗?看场景
数组法比 HashMap 快 3–5 倍,但这个差距只在百万级字符以上才明显;日常日志分析、配置解析这类几千字符的场景,用 Map 完全无感,反而更少出错。
立即学习“Java免费学习笔记(深入)”;
- 纯 ASCII 小文本(Map,代码干净,维护成本低
- 高频批量处理英文日志(如 Nginx access log):用
int[128],避免 GC 和哈希计算开销 - 含中文的批处理(如用户昵称统计):优先用
codePoints()+HashMap<integer integer></integer>,别省那点内存 - 注意:
HashMap初始化时设好容量(如new HashMap(32)),避免扩容重哈希影响性能
实际用哪一种,取决于你手上的字符串长什么样、有没有非 ASCII 字符、以及跑在什么量级上。数组法不是不能用,而是容易在“以为全是英文”的假设里翻车。










