相同单词未合并是因为未统一大小写、未清除标点及空格残留,导致“hello!”与“hello”等被视为不同键;需tolowercase()、正则清理、判空后计数,并慎用scanner分隔符。

用 HashMap 统计单词频次时,为什么相同单词没合并?
常见错误是把 Scanner 读出的每个 next() 结果直接当键存进 HashMap,但忽略了大小写、标点和空格残留。比如 “Hello!” 和 “hello” 被当成两个词;“word,” 和 “word” 也被视为不同键。
- 先调用
toLowerCase()统一大小写 - 用正则
replaceAll("[^a-z]", "")剥离标点和数字(注意:空字符串会因此产生,需跳过) - 检查
word.length() > 0再计数,避免空白键污染结果 - 别用
nextLine()直接拆——它不按词切分,容易把整行当一个“词”
Scanner 分词逻辑和 useDelimiter() 的实际影响
Scanner 默认以空白符(空格、制表、换行)为分隔符,看似合理,但遇到连字符、撇号、缩写(如 “don’t”, “state-of-the-art”)就会断错。强行用 useDelimiter("\W+") 看似能解决,但会导致开头/结尾非字母字符被吃掉,且连续标点可能生成空串。
- 推荐用
useDelimiter("[^a-zA-Z]+")—— 更精准保留纯字母词 - 如果文本含带撇号的英文词(如 “it’s”),得用
useDelimiter("[^a-zA-Z']+),再额外清理首尾单引号 - 每次调用
hasNext()前确保scanner没到末尾,否则next()抛NoSuchElementException
HashMap 计数的三种写法,哪一种最稳?
新手常写 map.put(word, map.get(word) + 1),但 map.get(word) 返回 null 时会触发 NullPointerException。自动装箱救不了原始类型语义缺失的问题。
- 安全写法:用
map.merge(word, 1, Integer::sum)—— JDK 8+,原子、简洁、空值安全 - 兼容老版本:先
map.containsKey(word)判断,再put或replace - 别用
getOrDefault(word, 0)后再put—— 非线程安全,且多一次哈希查找
中文文本或混合内容下,HashMap + Scanner 还适用吗?
不适用。Scanner 的 delimiter 机制依赖字符边界,对中文是“字”不是“词”,直接分出会得到单字而非语义词(如“苹果手机”切成“苹”“果”“手”“机”)。而且 HashMap 本身不处理同义、简繁、拼音归一化。
- 纯中文统计必须换分词库,比如
hanlp或jieba(Java 封装版) - 中英混合场景,先用正则粗筛出英文段落单独处理,中文部分走专业分词
- 哪怕只是过滤掉中文、只统计英文单词,也要注意 Unicode 字符范围——
[^a-zA-Z]不拦得住中文,得用[^\p{L}]+(\p{L}匹配所有字母类 Unicode 字符)










