1. 简介
在本篇文章中,我们将深入探讨在 Java 中创建不可变集合(Immutable Set)的不同方式。
不过,在开始之前,我们先明确一下什么是不可变集合,以及为什么我们需要它。
2. 什么是不可变集合?
一般来说,不可变对象是指一旦创建后,其内部状态就不能再被修改的对象。这种特性使得它天生就是线程安全的。同样的道理也适用于不可变集合。
举个例子,假设我们有一个包含若干值的 HashSet 实例。如果我们把它变成不可变集合,就相当于得到了一个“只读”版本的 Set。因此,任何试图修改它的操作都会抛出 UnsupportedOperationException 异常。
那么,我们为什么需要不可变集合呢?
✅ 最常见的使用场景就是在多线程环境下。我们可以放心地在线程之间共享不可变数据,而无需担心同步问题。
⚠️ 但需要注意的是:不可变性只作用于集合本身,而不作用于集合中的元素。也就是说,虽然不能往集合里添加或删除元素,但我们仍然可以修改集合中对象的属性(前提是这些对象本身是可变的)。
3. 使用核心 Java 创建不可变集合
如果我们只使用 Java 标准库提供的类,可以通过 Collections.unmodifiableSet() 方法来包装原始 Set,从而得到一个不可变集合。
首先,创建一个简单的 HashSet 并初始化一些字符串值:
Set<String> set = new HashSet<>();
set.add("Canada");
set.add("USA");
接着,用 Collections.unmodifiableSet()
包装这个 Set:
Set<String> unmodifiableSet = Collections.unmodifiableSet(set);
最后,为了验证我们的 unmodifiableSet
是真正的不可变集合,我们可以写一个简单的测试用例:
@Test(expected = UnsupportedOperationException.class)
public void testUnmodifiableSet() {
// 创建并初始化 Set 实例
Set<String> set = new HashSet<>();
set.add("Canada");
set.add("USA");
Set<String> unmodifiableSet = Collections.unmodifiableSet(set);
unmodifiableSet.add("Costa Rica"); // 尝试添加元素,应该抛异常
}
不出所料,测试会成功运行。对 unmodifiableSet
调用 add()
方法时会抛出 UnsupportedOperationException。
⚠️ 但是,如果我们在原始的 set
上添加新元素会发生什么呢?
set.add("Costa Rica");
此时,由于 unmodifiableSet
只是对原始 Set 的一种“视图”,它也会反映出这个变化。当我们打印 unmodifiableSet
的内容时:
[Canada, USA, Costa Rica]
可以看到,“Costa Rica” 已经出现在了不可变集合中。这说明 Collections.unmodifiableSet()
并不是深拷贝,只是做了个包装。
4. Java 9 中创建不可变集合的方式
从 Java 9 开始,引入了新的静态工厂方法 Set.of(elements)
来创建不可变集合:
Set<String> immutable = Set.of("Canada", "USA");
这种方式非常简洁直观,并且返回的集合是完全不可变的 —— 不仅不能增删改元素,甚至连原始集合的变化也不会影响它。
5. 使用 Guava 创建不可变集合
另一种常用的创建不可变集合的方法是使用 Google Guava 库中的 ImmutableSet 类。
Guava 的 ImmutableSet
会将原集合的数据复制到一个新的不可变实例中。因此,即使我们后续修改了原始 Set,ImmutableSet
内部的内容也不会受到影响。
和标准 Java 一样,对 ImmutableSet
的任何修改操作都会抛出 UnsupportedOperationException。
下面介绍几种常见的创建方式。
5.1. 使用 ImmutableSet.copyOf()
顾名思义,ImmutableSet.copyOf()
会把传入集合的所有元素复制一份,生成一个新的不可变集合:
Set<String> immutable = ImmutableSet.copyOf(set);
即使之后我们向原始 set
添加了新元素:
set.add("Costa Rica");
immutable
集合仍然保持不变:
[Canada, USA]
✅ 这才是真正意义上的“不可变”。
5.2. 使用 ImmutableSet.of()
类似于 Java 9 的 Set.of()
,Guava 提供了 ImmutableSet.of()
来直接创建不可变集合:
Set<String> immutable = ImmutableSet.of("Canada", "USA");
如果不传参数,则返回一个空的不可变集合。
📌 注意:这种方式与 Java 9 的 Set.of()
功能类似,但在某些细节上略有不同(如 null 值处理、泛型支持等),可以根据项目需求选择使用。
6. 总结
在这篇文章中,我们详细介绍了 Java 中不可变集合的概念及其重要性,并展示了三种主要的创建方式:
- ✅ 使用
Collections.unmodifiableSet()
(Java 标准库) - ✅ 使用
Set.of()
(Java 9+) - ✅ 使用 Guava 的
ImmutableSet
每种方式都有其适用场景:
方式 | 是否真正不可变 | 是否支持并发访问 | 是否依赖第三方库 |
---|---|---|---|
Collections.unmodifiableSet() |
❌(仅包装) | ✅ | ❌ |
Set.of() |
✅ | ✅ | ❌ |
ImmutableSet |
✅ | ✅ | ✅ |
最后,本文所有代码示例都可以在 GitHub 上找到。