Java 中的不可变列表

2024年9月10日 | 阅读 9 分钟

在 Java 中,不可变列表(Immutable List)是指一旦创建就无法修改的列表。尝试在创建后添加、删除或修改列表中的元素都会抛出异常。

使用不可变列表的主要好处在于它们提供了线程安全性,并使代码更加健壮。由于列表在创建后无法修改,因此不存在多个线程同时尝试修改它而导致问题的风险。此外,不可变列表可以轻松地在程序的各个部分共享,而无需担心被意外修改。

总的来说,在 Java 中使用不可变列表可以提高程序的安全性和健壮性,尤其是在共享数据结构可能导致问题的多线程环境中。

类声明

在 Java 中,ImmutableList 类是 Guava 库的一部分,该库提供了多种不可变集合类。要使用 ImmutableList,我们首先需要导入包含 ImmutableList 类的 com.google.common.collect 包。

ImmutableList 类的声明如下:

ImmutableList 继承自 ImmutableCollection 类,并实现了 List 接口。它是一个泛型类,这意味着我们可以创建任何数据类型的 ImmutableList。声明中的 E 代表类型参数,我们可以将其替换为任何类或接口名称。

类层次结构

ImmutableList 类实现了 List 接口,并表示一个创建后无法修改的列表。

ImmutableList 的类层次结构如下:

在这里,ImmutableCollection 是一个抽象类,它提供了 ImmutableCollection 接口的骨架实现,ImmutableList 继承自该接口。

总而言之,ImmutableList 类提供了一种方便高效的方式来创建和使用 Java 中的不可变列表。

创建 ImmutableList

在 Java 中,创建 ImmutableList 有多种方法,具体取决于您使用的 Java 版本和可用的库。以下是一些示例:

1. 使用 Java 9 的 of() 方法

Java 9 在 List 接口中引入了一个名为 of() 的新方法,该方法可以更简洁、更易读地创建不可变列表。of() 方法是一个工厂方法,它接受可变数量的参数,并返回一个包含这些元素的不可变列表。它可以与任何实现 List 接口的类一起使用,包括 ArrayList、LinkedList 和 ImmutableList。使用 of() 方法的一个优点是它更加简洁,并通过对参数执行类型推断来提供编译时安全性,确保只将正确类型的对象添加到 List 中。总而言之,of() 方法简化了 Java 中不可变列表的创建。

查找解决方案的步骤如下所示:

  1. 从 Java.util 包导入 List 类:这允许程序使用 List 接口来创建和操作对象列表。
  2. 使用 Java 9 工厂方法创建不可变列表:代码使用 List.of() 方法创建了一个包含四个元素的字符串不可变列表。
  3. 尝试修改列表:程序尝试使用 add() 方法向不可变列表添加元素,这是不允许在不可变列表上执行的。因此,程序会捕获 add() 方法抛出的 UnsupportedOperationException,并打印一条消息,表明列表无法修改。

文件名: ImmutableListExample.java

输出

Fruits: [apple, banana, orange, grape]
Cannot modify immutable List.

2. 使用 Guava 库中的 ImmutableList.Builder 类

以流畅的风格向 List 添加元素,方便逐步创建列表。

无论使用哪种方法,都可以像访问其他列表一样访问和迭代生成的 ImmutableList,但其内容无法修改。

以下是给定代码的逐步解决方案:

  1. 导入所需的类:从 com.google.common.collect 包导入 List 接口和 ImmutableList 类。
  2. 使用构建器创建不可变列表:使用 ImmutableList 构建器创建不可变列表。使用 add() 方法向 List 添加元素,然后调用 build() 方法创建不可变列表。
  3. 从现有列表创建不可变列表:创建一个包含所需元素的 List 对象。然后调用 ImmutableList.copyOf() 方法,将 List 作为参数传递,以创建不可变列表。
  4. 添加更多元素:使用 ImmutableList 构建器通过 addAll() 方法添加更多元素,然后调用 build() 方法创建不可变列表。
  5. 打印列表:使用 System.out.println() 方法打印不可变列表的内容。

实施

文件名: ImmutableListExample.java

输出

Immutable List 1: [Welcome, to, home]
Immutable List 2: [Welcome, to, home, Think]
Immutable List 3: [Welcome, to, home, Think, Big]

3. 通过使用 ImmutableList 类的 of() 方法

Guava 库中的 ImmutableList 类的 of() 方法允许您创建一个具有固定元素数量的不可变列表。一旦列表创建完成,您就无法添加、删除或修改其元素。

文件名: ImmutableListExample.java

输出

Fruits: [apple, banana, orange, grape]

4. 通过使用 copyOf() 方法

在 Java 中,copyOf() 方法会创建一个新数组,该数组会复制一个现有数组并指定新数组的长度。该方法接受两个参数:要复制的数组和新数组的长度。

文件名: ImmutableListExample.java

输出

Immutable List 1: [Java, Python, C++]
Immutable List 2: [Learning, Web, Development, is, Fun]

5. UnsupportedOperationException

该程序演示了如何使用 Collections.unmodifiableList 方法在 Java 中创建不可变列表。此外,它还展示了如何处理尝试修改列表时抛出的 UnsupportedOperationException。

查找解决方案的步骤如下所示:

  1. 首先,我们使用 of 方法创建一个包含一些初始元素的、可变的 ArrayList,该方法返回一个不可变列表。然后,我们将此 ArrayList 传递给 Collections.unmodifiableList 方法,该方法返回该列表的不可修改视图。
  2. 我们尝试使用 add、remove 和 set 方法修改不可变列表。由于列表是不可变的,尝试修改它将抛出 UnsupportedOperationException。
  3. 我们捕获抛出的 UnsupportedOperationException,并在控制台打印一条消息,指示尝试了哪个操作并且该操作不成功。

请注意,Collections.unmodifiableList 方法仅创建原始列表的不可修改视图。如果原始列表被修改,不可修改视图将反映这些更改。要创建一个真正不可修改且无法通过任何方式修改的列表,您可以使用 List 接口的自定义实现,该实现会在尝试修改列表时抛出异常。

实施

文件名: ImmutableListExample.java

输出

UnsupportedOperationException: null
UnsupportedOperationException: null
UnsupportedOperationException: null

6. Collections.unmodifiableList()

Collections.unmodifiableList() 是 Java 集合框架中的一个方法,它创建现有列表的不可修改视图。可以推断,尝试修改不可修改的列表将导致发生 UnsupportedOperationException。原始列表仍然可以被修改,并且任何更改都将反映在不可修改的列表中。

该程序展示了如何使用 Collections.unmodifiableList() 方法生成可变列表的不可修改表示。

查找解决方案的步骤如下所示:

  1. 创建一个可变的列表 mutableList,并使用 ArrayListadd() 方法向其中添加一些元素。
  2. 使用 unmodifiableList() 方法创建 mutableList 的不可修改视图,并将其赋值给变量 unmodifiableList
  3. 尝试使用 add() 方法修改不可变的列表 unmodifiableList。由于不可变列表是只读的,这将抛出 UnsupportedOperationException。捕获此异常后,会在控制台打印一条消息。
  4. 通过使用 add() 方法向原始可变列表 mutableList 添加另一个元素来修改它。
  5. 将可变列表和不可变列表都打印到控制台,以显示不可变列表反映了对原始可变列表所做的更改。

文件名: UnmodifiableListExample.java

输出

Attempt to modify unmodifiableList failed: null
mutableList: [apple, banana, orange, pear]
unmodifiableList: [apple, banana, orange, pear]

ImmutableList 的优点

ImmutableList 具有多种优点,包括:

  1. 线程安全性:由于 ImmutableList 是不可变的,因此它天然就是线程安全的。多个线程可以访问同一个列表,而不会有任何线程干扰或内存不一致的风险。
  2. 安全性:不可变列表不易受到安全漏洞的攻击。例如,如果攻击者尝试通过添加或删除元素来修改列表,他们将无法做到,因为列表是不可变的。
  3. 性能:由于不可变列表是只读的,因此可以对其进行缓存以提高性能。如果一个列表需要被多次访问,使用不可变列表而不是每次都创建一个新列表可以帮助避免开销。
  4. 可预测性:由于不可变列表不能被修改,因此它们的行为是可预测的。使用不可变列表的一些好处是消除了对防御性编程的需求,并使代码更容易理解。
  5. 简化编码:不可变列表通过消除对同步、防御性复制和易出错的手动内存管理的需要,简化了编码。这种方法的结果是代码更容易维护,外观也更整洁。
  6. 便于测试:由于不可变列表的内容不会改变,因此它们更容易测试。这种方法使得编写涵盖所有可能场景和边缘情况的测试更加容易。