本文目录导读:

Java 集合工厂方法(List.of()、Set.of()、Map.of() 等,Java 9+)非常好用,尤其适用于创建不可变、元素数量固定的集合,从工程实践角度看,它们能显著提升代码的简洁性、安全性和性能。
下面从几个核心维度帮你分析,方便你判断在什么场景下使用:
核心优点
-
极致的简洁与可读性
- 传统方式:
List<String> list = new ArrayList<>(); list.add("a"); list.add("b");或Arrays.asList("a", "b")。 - 工厂方式:
List<String> list = List.of("a", "b");。 - 尤其适合构造测试数据、常量集合或临时集合,代码一目了然。
- 传统方式:
-
完全的不可变性
- 创建的集合是不可变的,不能调用
add、remove、set等方法,否则会抛出UnsupportedOperationException。 - 这是最大的价值之一,在多线程环境下,无需考虑同步问题;在 API 设计中,返回这种集合可以安全地确保调用方不会意外修改内部数据。
- 创建的集合是不可变的,不能调用
-
自动防御性拷贝
- 相比
Arrays.asList()返回的“可修改的视图”,工厂方法创建的集合不依赖原数组,如果你需要返回一个内部数据结构的快照,return List.of(...)比手动new ArrayList<>(internalList)更简洁且能防止外部修改。
- 相比
-
性能与内存优化
- JDK 内部针对
of()方法有专门优化。List.of()对于 0 个、1 个、2 个元素等,会返回不同的、经过高度特化的内部类(如ListN、List12),通常比通用的ArrayList或Arrays.asList更节省内存。
- JDK 内部针对
-
元素校验
- 工厂方法不接受
null元素,如果你传入null,会立刻抛出NullPointerException,这能帮助你在代码早期就发现潜在的空指针问题,避免数据进入深层逻辑后出问题。
- 工厂方法不接受
常见陷阱与注意事项
-
禁止 null 元素
- 这是最大的限制,如果你的集合逻辑上允许
null,则不能使用工厂方法,需要退回到new ArrayList<>()或new HashSet<>()。
- 这是最大的限制,如果你的集合逻辑上允许
-
不支持加、删、改操作
- 如果你在后续代码中需要修改集合(例如添加日志、条件过滤),不能使用工厂方法,你需要将其复制到一个可变集合中:
var immutableList = List.of("a", "b"); var mutableList = new ArrayList<>(immutableList); // 或 List.copyOf() 的逆操作
- 如果你在后续代码中需要修改集合(例如添加日志、条件过滤),不能使用工厂方法,你需要将其复制到一个可变集合中:
-
Map.of() 的限制(Java 9-8)
Map.of()最多只支持 10 个键值对(Map.of(k1,v1, k2,v2, ..., k10,v10)),超过 10 个键值对,需要改用Map.ofEntries(),写法稍复杂:Map.ofEntries( Map.entry("key1", "val1"), Map.entry("key2", "val2"), // ... 不限数量 );
-
只读 ≠ 深度不可变
- 工厂方法保证的是集合结构的不可变(不能增删元素),但如果元素本身是可变对象(如
Date、ArrayList或内部有setter的自定义类),你仍然可以修改这些对象的状态,它提供的是“浅层”不可变。
- 工厂方法保证的是集合结构的不可变(不能增删元素),但如果元素本身是可变对象(如
与 Arrays.asList() 的关键区别
| 特性 | List.of() |
Arrays.asList() |
new ArrayList<>() |
|---|---|---|---|
| 大小 | 不可变 | 固定大小 | 可变大小 |
| 修改操作 | 不支持 set、add、remove |
支持 set,不支持 add/remove |
全部支持 |
| Null 支持 | 不支持 | 支持 | 支持 |
| 底层实现 | 工厂内部类 | 数组视图 | 动态数组 |
| 与数组的关系 | 独立 | 共享底层数组 | 独立 |
| 使用场景 | 常量、枚举、安全返回、函数式 | 遗留代码、需要 set 操作的场景 |
大部分通用场景 |
最佳实践建议
- 强推荐:定义常量集合时,优先使用
List.of()/Set.of()/Map.of()。 - 强推荐:在 API 中返回一个内部集合时,使用
Collections.unmodifiableList(...)或List.copyOf()。List.copyOf()内部会调用工厂方法,效果一致。 - 推荐:在测试代码中大量使用,快速构造测试数据。
- 谨慎使用:在编写函数式/流式操作时,可以用它对现有流进行过滤、转换后生成不可变结果:
var result = stream.filter(...).collect(Collectors.toUnmodifiableList());
- 考虑替代方案:如果需要可变、允许 null、或超过 10 对的
Map,用new HashMap<>()更合适;如果需要更大的灵活性和易用性(如 Guava 的ImmutableList、Maps.newHashMap()等),第三方库(如 Guava)提供了类似但更丰富的功能,Guava 的不可变集合语义更严格(ImmutableList.of()同样不允许 null)。
非常好用,但需要明确其不可变和无 null 的契约。
- 优点:简洁、安全(不可变、拒绝 null)、性能佳。
- 缺点:不可变、无 null、Map 键值对有限制。
只要你的场景符合“构造后就不变”这一条件(这在实际开发中比想象中更多),就放心大胆地用,它带来的代码清晰度和安全性提升非常明显,已在 Java 社区成为主流写法。