getordefault更安全因其是原子操作,避免多线程下「检查-执行」竞态;它不修改map,仅在键不存在时返回默认值,适用于只读场景;默认值需类型匹配且避免副作用。

getOrDefault 为什么比先 containsKey 再 get 更安全
直接调用 getOrDefault 能避免多线程环境下「检查-执行」竞态:比如先 containsKey 返回 true,紧接着另一个线程把键删了,再调用 get 就返回 null。而 getOrDefault 是原子操作,底层只查一次哈希桶。
常见错误是误以为它会自动 put 默认值——它不会。只是读取时返回默认值,原 map 不变。
- 适用于只读场景或默认值无副作用(如
0、""、new ArrayList()) - 若需「不存在则初始化并存入」,应改用
computeIfAbsent - 注意泛型擦除:传入的默认值类型必须和 value 泛型一致,否则编译报错
字符串计数中 getOrDefault 的典型写法
统计单词频次是最常见的使用场景,容易踩的坑是默认值写成 1 却忘了加一:
Map<string integer> count = new HashMap();
for (String word : words) {
count.put(word, count.getOrDefault(word, 0) + 1);
}</string>
这里 getOrDefault(word, 0) 返回当前计数(存在)或 0(不存在),再统一加 1 后 put 回去。
- 别写成
count.getOrDefault(word, 1) + 1——首次出现会变成 2 - 如果 value 是可变对象(如
StringBuilder),默认值必须是新实例,否则所有缺失键共享同一对象 - 性能上比两次查找略优,但差异微小;主要价值在语义清晰和线程安全
当默认值需要动态构造时,别硬塞进 getOrDefault
getOrDefault 的第二个参数是立即求值的表达式,哪怕键存在也会执行。比如:
// ❌ 即使 key 存在,new ExpensiveObject() 仍会被调用 map.getOrDefault(key, new ExpensiveObject());
这会导致不必要的对象创建和资源消耗。
- 此时应改用
computeIfAbsent:只在键缺失时才执行 lambda - 或者手动判断:
Integer val = map.get(key); return val != null ? val : computeDefault(); - 尤其注意日志、IO、数据库连接等有副作用的操作,绝不能放在
getOrDefault参数里
与 computeIfAbsent 的分工边界在哪
getOrDefault 只读不写,computeIfAbsent 缺失时写入并返回。二者不是替代关系,而是职责不同:
- 要「查一个数,没有就用 0」→ 用
getOrDefault(key, 0) - 要「查一个列表,没有就新建一个空列表并放进去」→ 用
computeIfAbsent(key, k -> new ArrayList()) - 如果误用
getOrDefault配合new ArrayList(),每次调用都新建对象,且不存入 map,后续再查仍是空列表
最易忽略的是:getOrDefault 的默认值不参与 map 结构变更,它只是个“兜底返回值”,连是否为 null 都不影响 map 本身。










