0

0

Java中如何使用Collections.unmodifiableList

P粉602998670

P粉602998670

发布时间:2025-09-18 14:40:01

|

576人浏览过

|

来源于php中文网

原创

Collections.unmodifiableList返回一个禁止修改操作的列表视图,原始列表的变更仍会反映其中,适用于保护数据完整性但需注意其非深拷贝、不阻止元素内部状态修改等特性。

java中如何使用collections.unmodifiablelist

Collections.unmodifiableList
在 Java 中用来创建一个只读的列表视图。它本身并不复制原始列表的数据,而是返回一个包装器,这个包装器阻止了通过它对底层列表进行任何修改操作(比如添加、删除、设置元素)。这意味着,如果你尝试通过这个视图修改列表,你会得到一个
UnsupportedOperationException
。它的核心价值在于提供一种安全机制,让你能将内部列表暴露给外部代码,同时又保证内部数据不会被意外或恶意地修改。

解决方案

使用

Collections.unmodifiableList
其实非常直接。你只需要将你想要保护的
List
对象作为参数传入它的静态方法即可。

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class UnmodifiableListDemo {
    public static void main(String[] args) {
        // 1. 创建一个普通的ArrayList
        List mutableList = new ArrayList<>();
        mutableList.add("Apple");
        mutableList.add("Banana");
        mutableList.add("Cherry");

        System.out.println("原始列表: " + mutableList); // 输出: 原始列表: [Apple, Banana, Cherry]

        // 2. 创建一个不可修改的列表视图
        List unmodifiableView = Collections.unmodifiableList(mutableList);
        System.out.println("不可修改视图: " + unmodifiableView); // 输出: 不可修改视图: [Apple, Banana, Cherry]

        // 3. 尝试通过不可修改视图修改列表 (会抛出异常)
        try {
            unmodifiableView.add("Date"); // 这行会抛出 UnsupportedOperationException
        } catch (UnsupportedOperationException e) {
            System.out.println("尝试修改不可修改视图失败: " + e.getMessage());
        }

        // 4. 原始列表的修改会反映在不可修改视图中
        mutableList.add("Elderberry");
        System.out.println("修改原始列表后,不可修改视图: " + unmodifiableView); // 输出: 修改原始列表后,不可修改视图: [Apple, Banana, Cherry, Elderberry]

        mutableList.remove("Banana");
        System.out.println("再次修改原始列表后,不可修改视图: " + unmodifiableView); // 输出: 再次修改原始列表后,不可修改视图: [Apple, Cherry, Elderberry]
    }
}

代码里很清楚地展示了,

unmodifiableView
本身不能被修改,但它始终反映着
mutableList
的最新状态。这正是它“视图”的本质。在我看来,理解这一点是正确使用它的关键。

为什么我们需要一个“不可修改”的列表?它和“常量”有什么区别

这个问题问得很有深度,因为它触及了软件设计中的一个核心原则:防御性编程和数据封装。我们之所以需要一个“不可修改”的列表,最直接的原因就是为了保护数据完整性。想象一下,你有一个方法,它返回了一个内部状态的列表。如果外部代码拿到这个列表后随意修改,你的内部状态就会变得不可预测,这在多线程环境下尤其危险,可能导致难以追踪的 bug。通过返回一个

unmodifiableList
,你实际上是在说:“你可以看,但不能动。”这是一种非常有效的契约。

立即学习Java免费学习笔记(深入)”;

至于它和“常量”的区别,这可能是很多初学者容易混淆的地方。当我们说一个变量是

final
的,比如
final List myList = new ArrayList<>();
,这仅仅意味着
myList
这个引用不能再指向其他列表对象了。但是,
myList
所指向的那个
ArrayList
对象本身还是可以被修改的,你可以继续
myList.add("New Item")
或者
myList.remove(0)
final
关键字约束的是引用本身,而
Collections.unmodifiableList
约束的是列表内容(通过这个视图)的可修改性。

在我个人的开发经验中,这种机制在 API 设计中尤其重要。比如,一个类内部维护着一些配置项列表,或者缓存列表。如果你直接把内部的

ArrayList
返回出去,外部调用者一个
clear()
操作就能把你整个配置清空,那可真是灾难。这时候,用
unmodifiableList
包装一下再返回,就能有效避免这类问题。

使用
unmodifiableList
时有哪些常见的误解和陷阱?

我见过不少开发者在使用

unmodifiableList
时踩过坑,这主要是因为对它的工作原理理解不够透彻。

一个非常常见的误解就是:它创建了一个全新的、独立的列表副本。 实际上,它只是一个包装器,一个只读的“窗口”。这意味着,如果原始列表在创建不可修改视图之后被修改了,那么这个不可修改视图也会反映出这些修改。上面代码示例中

mutableList.add("Elderberry")
之后
unmodifiableView
也会更新就证明了这一点。如果你想要一个完全独立的、不可变的副本,你需要先创建一个副本,比如
new ArrayList<>(originalList)
,然后再用
Collections.unmodifiableList
包装这个副本。

另一个容易被忽视的细节是:它只保证列表结构本身不可变,不保证列表中的元素不可变。 如果你的列表存储的是可变对象(比如你自定义的

Person
对象,它有
setName()
方法),那么即使列表是不可修改的,你仍然可以通过获取列表中的
Person
对象,然后调用
person.setName("New Name")
来修改这个对象的状态。
unmodifiableList
阻止的是
add
,
remove
,
set
等操作,它管不着你对列表内部对象引用的修改。要实现真正的深度不可变性,你需要确保列表中的所有元素本身也是不可变的,或者在返回时进行深度拷贝。

还有一点,虽然不常见,但值得一提:

Collections.unmodifiableList
返回的实际上是一个内部类实例,它通常不是
Serializable
。如果你需要序列化一个不可修改的列表,你可能需要先将其转换为一个
Serializable
的列表实现(比如
ArrayList
),然后再进行序列化。

这些“陷阱”并非

unmodifiableList
的设计缺陷,而是我们作为开发者需要理解其边界和工作方式。在我看来,Java 集合框架的这种设计哲学,既提供了灵活性,也要求我们对底层机制有清晰的认知。

除了
Collections.unmodifiableList
,Java还有哪些提供不可变性的方式?它们各有什么适用场景?

Java 生态中,提供不可变性的方式远不止

Collections.unmodifiableList
这一种,它们各有千秋,适用于不同的场景。

1.

List.of()
,
Set.of()
,
Map.of()
(Java 9+):
这是 Java 9 引入的工厂方法,用于创建真正意义上的不可变集合。它们返回的集合在创建后就不能再修改,任何修改操作都会抛出
UnsupportedOperationException

  • 优点: 简洁、高效,创建的集合是真正的不可变,线程安全。
  • 缺点: 不允许
    null
    元素,一旦创建就不能改变,不支持动态添加或删除。
  • 适用场景: 创建小型、固定内容的集合,例如配置项列表、常量集合等。如果你确定集合内容不会改变,并且不需要支持
    null
    ,这是最佳选择。
List immutableFruits = List.of("Apple", "Banana", "Cherry");
// immutableFruits.add("Date"); // 抛出 UnsupportedOperationException

2. Guava 库的 Immutable Collections: Google Guava 库提供了更强大、更全面的不可变集合类,如

ImmutableList
,
ImmutableSet
,
ImmutableMap
等。它们通过
Builder
模式提供了更灵活的构建方式。

  • 优点: 真正的不可变,线程安全,提供了
    Builder
    模式方便复杂集合的构建,对
    null
    元素有明确的拒绝策略(通常不允许)。性能通常优于
    Collections.unmodifiableList
    包装的
    ArrayList
  • 缺点: 需要引入第三方库。
  • 适用场景: 当你需要构建复杂的、高度不可变的集合,并且对性能和健壮性有较高要求时。在大型项目中,Guava 的不可变集合是我的首选。
import com.google.common.collect.ImmutableList;

ImmutableList guavas = ImmutableList.builder()
    .add("Guava")
    .add("Mango")
    .build();
// guavas.add("Papaya"); // 抛出 UnsupportedOperationException

3. 自定义不可变类: 对于更复杂的数据结构,你可以自己设计不可变类。这意味着类的所有字段都是

final
的,并且如果字段是可变对象,需要进行防御性拷贝。

  • 优点: 完全控制不可变性,可以根据业务需求定制。
  • 缺点: 开发成本高,需要仔细设计以确保所有路径都保持不可变。
  • 适用场景: 当内置集合无法满足你的复杂业务需求,或者你需要创建包含多种类型数据的复合不可变对象时。

在我看来,选择哪种方式,关键在于你对“不可变性”的需求程度和上下文。如果你只是想防止外部修改内部列表,

Collections.unmodifiableList
简单实用。如果你需要一个真正不可变且性能优异的固定集合,Java 9+ 的
List.of()
是个好选择。而对于更复杂的场景,Guava 的
ImmutableList
提供了更强大的工具集。理解这些选项,能帮助我们写出更健壮、更易于维护的代码。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
guava包作用
guava包作用

guava是一个java库,增强了java标准库,提供更有效率和易于使用的集合、实用程序、缓存和并发工具。想了解更多guava的相关内容,可以阅读本专题下面的文章。

261

2024.05.29

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

463

2023.08.02

c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

236

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

458

2024.03.01

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1501

2023.10.24

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

538

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

17

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

27

2026.01.06

俄罗斯Yandex引擎入口
俄罗斯Yandex引擎入口

2026年俄罗斯Yandex搜索引擎最新入口汇总,涵盖免登录、多语言支持、无广告视频播放及本地化服务等核心功能。阅读专题下面的文章了解更多详细内容。

158

2026.01.28

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 7.9万人学习

Java 教程
Java 教程

共578课时 | 52.7万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号