在Java里如何创建不可变List_不可变集合实现解析

Java 9+ 推荐用 List.of() 创建真正不可变List,它返回JVM内置私有不可变类,禁止null、所有修改操作抛异常且反射受限;Collections.unmodifiableList仅提供不可修改视图,原List仍可被修改。

Java中创建不可变List最直接的方式是使用 Collections.unmodifiableList() 或 Java 9+ 引入的 List.of()。但二者本质不同:前者是“不可修改视图”,后者才是真正的不可变实现。

用 List.of() 创建真正不可变List(推荐,Java 9+)

List.of() 返回的是 JVM 内置的紧凑、高效、不可实例化、无公开构造器的私有不可变类(如 ImmutableCollections.ListN)。它在创建时就禁止 null 元素,且所有修改操作(add/remove/set/clear)都会抛出 UnsupportedOperationException,连反射绕过都受限(内部类无 public 构造器,且元素数组 final)。

示例:

  • List list = List.of("a", "b", "c");
  • list.add("d"); // 运行时抛出 UnsupportedOperationException
  • List.of(); // 空不可变List
  • List.of(1, 2, 3); // 支持泛型推断

用 Collections.unmodifiableList() 包装可变List(需谨慎)

该方法返回一个包装器对象,底层仍持有对原List的引用。它只是拦截修改方法,**不阻止原List被其他引用修改**——也就是说,它提供的是“只读视图”,不是“不可变性”。

示例:

  • List mutable = new ArrayList(Arrays.asList("x", "y"));
  • List unmod = Collections.unmodifiableList(mutable);
  • unmod.add("z"); // 抛异常 ✅
  • mutable.add("z"); // 原List仍可改 ❌ → unmod.size() 变为3

若要安全使用,必须确保原List不再被其他代码访问(例如封装后立即丢弃原始引用)。

第三方库方案:Guava 的 ImmutableList

Guava 提供了功能更丰富的 ImmutableList,支持 builder 模式、自定义排序、copyOf、toImmutableList() 等。其内部也使用私有不可变实现,元素在构建时深拷贝(对引用类型仅拷贝引用,非深克隆),同样拒绝 null 和修改操作。

常用写法:

  • ImmutableList list = ImmutableList.of("a", "b");
  • ImmutableList.copyOf(array);
  • ImmutableList.builder().add("x").addAll(otherList).build();

注意:需引入 Guava 依赖,适合已有 Guava 生态的项目。

不可变性的关键细节

真正不可变 ≠ 仅仅不能 add/remove。还需关注:

  • 元素本身是否可变:若List里存的是可变对象(如 StringBuilder),即使List不可变,对象状态仍可被修改;不可变性不传递。
  • 线程安全性:不可变集合天然线程安全,无需同步。
  • 序列化安全:JDK 的 List.of() 和 Guava 的 ImmutableList 都正确实现了序列化协议,反序列化后仍是不可变实例。
  • 性能差异List.of() 零内存开销(小列表甚至内联存储),unmodifiableList 多一层代理对象,Guava 在构建阶段稍重但功能强。