0

0

Java中Collections.unmodifiableList的使用

P粉602998670

P粉602998670

发布时间:2025-09-21 20:35:01

|

960人浏览过

|

来源于php中文网

原创

Collections.unmodifiableList提供只读视图,防止外部修改列表结构,但底层列表变化仍会反映其中,适用于保护内部集合不被直接修改的API设计场景。

java中collections.unmodifiablelist的使用

Collections.unmodifiableList
在 Java 中提供了一种方式,让我们能够获取一个列表的“只读视图”。这意味着你可以读取列表中的内容,但无法通过这个视图添加、删除或修改元素。它对于构建健壮的 API 和实现防御性编程至关重要,能有效防止外部代码意外或恶意地修改你的内部数据结构。

要使用

Collections.unmodifiableList
,过程非常直接。你只需要将一个现有的
List
对象作为参数传递给它,它就会返回一个不可修改的
List
包装器。

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

public class UnmodifiableListExample {
    public static void main(String[] args) {
        List mutableList = new ArrayList<>();
        mutableList.add("Apple");
        mutableList.add("Banana");
        mutableList.add("Cherry");

        // 获取一个不可修改的视图
        List unmodifiableView = Collections.unmodifiableList(mutableList);

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

        // 尝试通过不可修改视图修改列表,会抛出 UnsupportedOperationException
        try {
            unmodifiableView.add("Date");
        } catch (UnsupportedOperationException e) {
            System.out.println("尝试通过不可修改视图添加元素失败: " + e.getMessage());
        }

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

        // 尝试通过不可修改视图设置元素
        try {
            unmodifiableView.set(0, "Apricot");
        } catch (UnsupportedOperationException e) {
            System.out.println("尝试通过不可修改视图设置元素失败: " + e.getMessage());
        }
    }
}

这段代码清楚地展示了,一旦你得到了

unmodifiableView
,任何尝试修改它的操作(如
add
,
remove
,
set
,
clear
等)都会导致
UnsupportedOperationException
。但需要特别注意的是,它仅仅是一个“视图”,这意味着如果原始的
mutableList
发生了改变,
unmodifiableView
也会实时反映这些改变。这并非一个独立的、不可变的副本,而是一个对原始列表的只读窗口。

在 Java API 设计中,何时应考虑返回一个不可修改的列表视图?

在设计 Java API 时,返回

Collections.unmodifiableList
视图是一个非常常见且推荐的做法,尤其是在你需要暴露内部集合给外部调用者,但又不希望外部代码能直接修改这些内部状态时。这其实是防御性编程的一个核心体现。

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

想象一下,你有一个服务类,它维护着一份重要的配置列表或者缓存数据。如果你的方法直接返回

List getConfigs()
,并且这个列表是内部状态的直接引用,那么任何调用者都可以获取这个列表,然后随意地
add()
remove()
甚至
clear()
它。这无疑会破坏你的服务内部的一致性,甚至引发难以追踪的 bug。通过返回
Collections.unmodifiableList(internalConfigs)
,你就明确地告诉了调用者:“这是我的配置,你可以看,但不能动。”

这不仅保护了你的内部数据不被意外篡改,也让你的 API 意图更加清晰。调用者一看返回类型就知道,这个列表是只读的,它会避免尝试修改它,从而减少了误用。此外,在多线程环境下,虽然

unmodifiableList
本身不能解决所有并发问题(因为底层列表可能仍然被其他线程修改),但它至少阻止了通过这个特定引用进行的修改操作,从而降低了某些类型的并发错误的风险。它是一种轻量级的保护机制,成本低廉,收益却很高。

Collections.unmodifiableList
与 Java 9+ 的
List.of()
或 Guava 的
ImmutableList
有何不同?

这三者都与“不可变”集合有关,但它们的工作原理和适用场景却有着本质的区别,理解这些差异对于避免潜在的 bug 至关重要。

Collections.unmodifiableList
,正如我们前面所讨论的,它提供的是一个不可修改的视图。它的核心特点是:

  1. 视图而非副本:它并没有创建新的列表,而是包装了你传入的原始列表。这意味着如果原始列表在之后被修改了,这个不可修改的视图也会反映出这些修改。
  2. 阻止结构性修改:它阻止的是对列表结构(添加、删除元素)的修改,但如果列表中的元素本身是可变对象,那么这些元素的内部状态仍然可以通过其他方式被修改。

举个例子:

List original = new ArrayList<>(Arrays.asList("Alpha", "Beta"));
List view = Collections.unmodifiableList(original);
original.add("Gamma"); // 原始列表被修改
System.out.println(view); // 输出: [Alpha, Beta, Gamma] - 视图也随之改变

而 Java 9 引入的

List.of()
Set.of()
等工厂方法,以及 Guava 库提供的
ImmutableList
(或
ImmutableSet
等),它们创建的是真正的不可变集合。它们的主要特点是:

OSPod.CMS专业建站平台
OSPod.CMS专业建站平台

专业级别的大型网站建站产品,JAVA技术的CMS管理系统,ospod提供上百套专业模板供您选择,包括审批工作流,流量统计和流行网络应用,是公司企业建设专业网站的首选产品,也使用于专业建站人士完成复杂网站项目。管理地址cmsadmin登陆用户名:ospod 密码:ospod1234

下载
  1. 不可变副本:它们在创建时会生成一个新的集合,这个集合是原始数据的一个“快照”或“副本”。一旦创建,其内容就永远不能被修改。
  2. 不随原始数据变化:即使你用来创建不可变集合的原始数据在之后发生了改变,这个不可变集合也不会受到影响。它是一个完全独立的、固定的实体。
  3. 线程安全(内容层面):由于内容不可变,它天然是线程安全的,无需额外的同步措施。
  4. 通常不允许 null 元素:这是为了避免空指针异常和简化逻辑。

对比示例:

List original = new ArrayList<>(Arrays.asList("Alpha", "Beta"));

// Collections.unmodifiableList (视图)
List view = Collections.unmodifiableList(original);

// Java 9+ ImmutableList (副本)
List immutableCopy = List.of("Alpha", "Beta"); // 或 ImmutableList.copyOf(original)

original.add("Gamma"); // 修改原始列表

System.out.println("原始列表: " + original); // [Alpha, Beta, Gamma]
System.out.println("不可修改视图: " + view); // [Alpha, Beta, Gamma] -- 随原始列表变化
System.out.println("不可变副本: " + immutableCopy); // [Alpha, Beta] -- 保持不变

何时选择哪个?

  • 当你希望防止外部修改,但允许内部修改并希望外部能看到这些修改时,使用
    Collections.unmodifiableList
    。它适用于返回内部状态的只读引用。
  • 当你需要一个内容绝对不会改变的集合,无论原始数据如何,并且希望获得线程安全和更好的缓存性时,使用
    List.of()
    ImmutableList
    。它适用于常量、配置或需要传递给多线程环境的数据。

使用
Collections.unmodifiableList
时有哪些常见的“陷阱”或误解?

尽管

Collections.unmodifiableList
功能明确,但在实际使用中,一些常见的误解和“陷阱”可能会导致意想不到的行为,甚至引发 bug。

一个最普遍的误解就是将其视为一个不可变的副本。前面我们已经强调过,它是一个视图。这个区别是所有陷阱的根源。如果你将一个

unmodifiableList
传递给另一个方法,而那个方法又通过某种方式获取到了原始列表的引用并对其进行了修改,那么你的
unmodifiableList
也会悄无声息地改变。这在调试时可能会非常令人困惑,因为你明明看到它“不可修改”,但内容却变了。

另一个重要的“坑”在于列表中元素的可变性

unmodifiableList
只能阻止对列表结构(即添加、删除、重新排序元素)的修改。但如果你的列表存储的是可变对象(例如,
List
),那么即使列表本身是不可修改的,你仍然可以通过获取列表中的元素,然后调用该元素的方法来修改其内部状态。

class MutableObject {
    String name;
    public MutableObject(String name) { this.name = name; }
    public void setName(String name) { this.name = name; }
    @Override public String toString() { return name; }
}

List mutableObjects = new ArrayList<>();
mutableObjects.add(new MutableObject("Obj1"));
List unmodifiableObjList = Collections.unmodifiableList(mutableObjects);

System.out.println("修改前: " + unmodifiableObjList); // 输出: [Obj1]
unmodifiableObjList.get(0).setName("NewObj1"); // 通过元素引用修改其内部状态
System.out.println("修改后: " + unmodifiableObjList); // 输出: [NewObj1]

这里,

unmodifiableObjList
并没有被修改(元素数量没变),但它包含的
MutableObject
的状态却改变了。要真正做到“不可变”,你需要确保列表中的所有元素也都是不可变的。

此外,序列化问题也值得注意。

Collections.unmodifiableList
返回的通常是
Collections
内部的私有静态类(例如
Collections.UnmodifiableRandomAccessList
Collections.unmodifiableList
)。这些内部类可能没有实现
Serializable
接口,或者即使实现了,其序列化和反序列化行为也可能不如标准的
ArrayList
LinkedList
那么稳定或预期。如果你需要序列化一个不可修改的列表,通常更安全的做法是先将其转换为一个普通的
ArrayList
LinkedList
的副本,然后再进行序列化。

最后,虽然通常不常见,但反射攻击理论上可以绕过

unmodifiableList
的保护。通过反射,你可以获取到
unmodifiableList
内部封装的原始列表引用,然后直接对原始列表进行修改。在大多数应用场景中,这并不是一个需要防御的实际威胁,但在面对恶意代码或高度安全敏感的环境时,需要意识到这种可能性。

理解这些“陷阱”有助于我们更明智地使用

Collections.unmodifiableList
,确保它在你的代码中发挥预期的作用,而不是埋下隐患。

热门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中文网学习。

1502

2023.10.24

treenode的用法
treenode的用法

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

539

2023.12.01

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

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

17

2025.12.22

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

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

28

2026.01.06

java入门学习合集
java入门学习合集

本专题整合了java入门学习指南、初学者项目实战、入门到精通等等内容,阅读专题下面的文章了解更多详细学习方法。

1

2026.01.29

热门下载

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

精品课程

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

共23课时 | 3万人学习

C# 教程
C# 教程

共94课时 | 7.9万人学习

Java 教程
Java 教程

共578课时 | 52.9万人学习

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

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