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库的项目 |
关键差异点:
内存占用:
- ✅ Guava创建副本:内存占用更高但线程安全
- ❌ JDK/Commons创建视图:内存占用低但依赖原始列表
类型安全:
- ✅ Guava返回
ImmutableList
:编译期类型检查 - ❌ 其他返回
List
:运行时才能发现修改尝试
- ✅ Guava返回
性能考虑:
- 小列表:差异可忽略
- 大列表:Guava的副本创建可能成为瓶颈
💡 最佳实践:优先使用Java 9+的
List.of()
,需要强类型保证时选择Guava,避免在性能敏感场景使用包装视图。
所有示例代码已上传至GitHub仓库,可直接导入Maven项目运行验证。