1. 概述

本文将系统介绍如何创建不可变的ArrayList,涵盖三种主流实现方式:核心JDK、Google Guava库和Apache Commons Collections 4。

⚠️ 注意:不可变(Immutable)与只读(Unmodifiable)有细微差别,本文中我们统一使用"不可变"这个更常用的术语。

2. 使用JDK原生实现

2.1 传统方式(Java 8及之前)

JDK提供了最直接的不可变列表创建方法:

Collections.unmodifiableList(list);

✅ 操作后列表将变为不可修改状态:

@Test(expected = UnsupportedOperationException.class)
public void 当使用JDK创建不可变列表时_尝试修改会抛出异常() {
    List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
    List<String> unmodifiableList = Collections.unmodifiableList(list);
    unmodifiableList.add("four"); // 踩坑点:这里会抛出UnsupportedOperationException
}

2.2 Java 9+ 新特性

Java 9引入了更优雅的静态工厂方法:

List.of(elements...)

⚠️ 使用时需注意参数类型转换:

@Test(expected = UnsupportedOperationException.class)
public final void 当使用Java9创建不可变列表时_尝试修改会抛出异常() {
    final List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
    final List<String> unmodifiableList = List.of(list.toArray(new String[]{}));
    unmodifiableList.add("four"); // 同样会抛出异常
}

💡 技巧:List.of()接受可变参数,所以需要先将ArrayList转为数组。

3. 使用Google Guava实现

Guava提供了专门的ImmutableList实现:

ImmutableList.copyOf(list);

✅ 创建后列表将完全不可变:

@Test(expected = UnsupportedOperationException.class)
public void 当使用Guava创建不可变列表时_尝试修改会抛出异常() {
    List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
    List<String> unmodifiableList = ImmutableList.copyOf(list);
    unmodifiableList.add("four"); // 踩坑警告:必然抛出异常
}

🔍 重要区别:Guava会创建全新副本而非视图,原始列表修改不会影响不可变列表。

3.1 使用Builder模式(推荐)

Guava的Builder模式提供更灵活的创建方式:

@Test(expected = UnsupportedOperationException.class)
public void 当使用GuavaBuilder创建时_返回强类型不可变列表() {
    List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
    ImmutableList<String> unmodifiableList = ImmutableList.<String>builder()
        .addAll(list)
        .build();
    unmodifiableList.add("four"); // 同样会抛出异常
}

✅ 优势:

  • 返回强类型ImmutableList而非普通List
  • 支持链式调用,代码更优雅
  • 明确表达不可变意图

4. 使用Apache Commons Collections实现

Commons Collections提供了类似功能:

ListUtils.unmodifiableList(list);

✅ 效果与JDK原生方法一致:

@Test(expected = UnsupportedOperationException.class)
public void 当使用CommonsCollections创建时_尝试修改会抛出异常() {
    List<String> list = new ArrayList<>(Arrays.asList("one", "two", "three"));
    List<String> unmodifiableList = ListUtils.unmodifiableList(list);
    unmodifiableList.add("four"); // 必然抛出异常
}

⚠️ 注意:此方法创建的是包装视图,原始列表修改仍会影响不可变列表。

5. 总结对比

实现方式 特点 适用场景
JDK原生 - Java 9+更简洁
- Java 8需额外转换
无第三方依赖时首选
Guava - 强类型不可变集合
- 创建副本而非视图
- Builder模式灵活
需要明确不可变语义时
Commons - 与JDK原生类似
- 包装视图模式
已使用Commons库的项目

关键差异点:

  1. 内存占用

    • ✅ Guava创建副本:内存占用更高但线程安全
    • ❌ JDK/Commons创建视图:内存占用低但依赖原始列表
  2. 类型安全

    • ✅ Guava返回ImmutableList:编译期类型检查
    • ❌ 其他返回List:运行时才能发现修改尝试
  3. 性能考虑

    • 小列表:差异可忽略
    • 大列表:Guava的副本创建可能成为瓶颈

💡 最佳实践:优先使用Java 9+的List.of(),需要强类型保证时选择Guava,避免在性能敏感场景使用包装视图。

所有示例代码已上传至GitHub仓库,可直接导入Maven项目运行验证。


原始标题:Immutable ArrayList in Java | Baeldung