这里以HashMap的computeIfAbsent为例
map.computeIfAbsent(key, key -> new ArrayList<>()).add(value);
✅ 为什么它是 Function 接口?
我们来看一下 Java 中 Map.computeIfAbsent 方法的定义:
default V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
- 第一个参数是
K key:要查找的键; - 第二个参数是
Function<? super K, ? extends V>:一个函数式接口; - 它的作用是:当这个 key 不存在时,用 key 去“计算”出一个值作为默认值返回并插入到 map 中。
原因
1. 统一抽象:将“生成值”的行为封装为函数
Java 设计者希望将“如何生成值”这一逻辑从 computeIfAbsent 方法中解耦出来,通过传入一个函数来决定:
- 如果 key 不存在,我该创建什么类型的值?
- 是否需要根据 key 来构造特定的值?(比如异位词分类)
- 是否需要做复杂的初始化逻辑?
这种设计使得 computeIfAbsent 不再关心“怎么创建”,只负责“什么时候调用”。
2. Lambda 简洁表达:避免冗长的匿名类写法
如果没有 Lambda 表达式,你需要这样写:
map.computeIfAbsent(key, new Function<String, List<String>>() {
@Override
public List<String> apply(String key) {
return new ArrayList<>();
}
});
简洁、清晰、易读。
3. 延迟执行(Lazy Evaluation)
mappingFunction.apply(key) 只有在 key 不存在时才会真正执行,这可以带来以下好处:
- 避免不必要的对象创建;
- 支持复杂或耗时的初始化逻辑(如数据库查询、网络请求等);
- 提高性能,只有真正需要时才初始化资源。
4. 符合函数式编程思想:组合与复用
你可以把 Function 抽象出来,用于多个地方复用:
Function<String, List<String>> listFactory = k -> new ArrayList<>();
map1.computeIfAbsent("a", listFactory);
map2.computeIfAbsent("b", listFactory);
也可以和其他函数组合使用(例如配合 compose 或 andThen),实现更灵活的数据处理流程。
5. 适用于各种 Map 分组统计场景
这是 LeetCode 和社区广泛采用的标准写法之一(来自记忆):
map.computeIfAbsent(key, k -> new ArrayList<>()).add(value);
适用场景
- 字符串按异位词分组;
- 数字按范围分类;
- 数据按标签归类;
- 实现多值 Map(MultiMap)功能;
- 构建树形结构、图结构的映射表等。
📚 总结
| 特性 | 说明 |
|---|---|
key -> new ArrayList<>() 是 Function 的 Lambda 实现 | 用于提供默认值 |
本质是 Function<K, List<V>> 接口 | 只有一个抽象方法 apply(K key) |
| 优势 | 延迟执行、避免空判断、提高代码可读性 |
| 使用场景 | Map 分组统计、多值映射、集合初始化 |
| 社区推荐 | 已成为 LeetCode 和 Java 社区标准写法 |