java 9的list.of和set.of创建真不可变集合,禁止任何修改操作并严格限制null和重复元素,适用于配置等静态数据,不兼容java 8及以下版本。

Java 9 List.of 和 Set.of 创建的是真不可变集合
它们不是“只读包装”,而是从底层就禁止修改的实现。调用 add、remove、clear 等任何修改方法,一律抛出 UnsupportedOperationException,且异常堆栈里不会出现你自己的代码——这是 JVM 层面的硬约束。
常见错误现象:
• 把 List.of(1, 2, 3) 传给一个期望可变 List 的老方法,运行时炸在 add() 调用处
• 误以为 set.add(x) 会静默失败,结果直接中断流程
- 使用场景:适合配置项、枚举值、路由规则等生命周期内绝不变动的数据
- 不能用于需要后续追加元素的临时容器(比如循环中累积结果)
- 如果必须“构造后修改”,得显式转成可变类型:
new ArrayList(List.of(...))
空值限制:Set.of 不允许 null,List.of 也不行
这两个工厂方法对 null 是零容忍的——哪怕只有一个元素是 null,创建时就立刻抛 NullPointerException,不是运行时才报错。
容易踩的坑:
• 从数据库查出字段为 NULL 的记录,直接塞进 List.of(row.field) → 立刻崩
• 用 Optional.orElse(null) 后没做空检查就进工厂方法
立即学习“Java免费学习笔记(深入)”;
-
Set.of("a", null)和List.of("a", null)都非法 - 替代方案:用
Arrays.asList或构造器手动建,但要自己承担空值风险 - 如果业务逻辑允许
null,别贪图工厂方法简洁,老实用new ArrayList()+add
重复元素:Set.of 检查严格,List.of 完全不管
Set.of 在创建时就遍历所有参数做重复校验,发现相同元素(按 equals 判定)直接抛 IllegalArgumentException;而 List.of 对重复完全放行,和普通列表行为一致。
典型问题:
• Set.of("a", "a") 报错,但 Set.of("a", new String("a")) 也报错(字符串字面量池不影响 equals)
• 用自定义对象时,若没重写 equals/hashCode,多个不同实例会被当成“不重复”而通过校验,但后续 contains 行为可能不符合预期
-
Set.of的重复检查发生在构造阶段,不依赖运行时状态 - 若需容忍重复或延迟去重,不要用
Set.of,改用new HashSet(list) - 注意:数组形式的重载(如
Set.of(array))不存在,必须展开参数
性能与兼容性:小集合快,大集合慎用,且 Java 8 及以下无法编译
这些工厂方法内部用了高度特化的紧凑实现(比如 ImmutableCollections.ListN),对 0–10 个元素做了极致优化;但超过 12 个元素后,底层其实是委托给 ArrayList 构造再包装,优势消失。
兼容性陷阱:
• 源码写了 List.of,但用 Java 8 编译器会直接报错:“cannot resolve method ‘of’”
• 即使运行在 Java 11 上,只要编译目标设为 --release 8,也会失败
- 构建脚本里确认
sourceCompatibility和targetCompatibility≥ 9 - Android 开发者注意:直到 API 33(Android 13)才部分支持,低版本会
NoSuchMethodError - 如果不确定运行环境,宁可用 Guava 的
ImmutableList.of,它兼容更广
真正麻烦的不是语法或功能,是它把“不可变”这个契约提前到编译期之后、运行期之前就锁死了——一旦选了,连反射绕过都无效,出问题只能回退设计。










