当前位置:首页 > 文章列表 > 文章 > java教程 > Java List.copyOf 怎样创建不可变副本

Java List.copyOf 怎样创建不可变副本

来源:17golang原创 2026-10-18 13:37:04 0浏览 收藏

在 Java 方法之间传递一组配置、权限或查询条件时,如果直接把 ArrayList 交给下游代码,接收方就可能改变原列表。List.copyOf 适合解决这个边界:它按源集合的迭代顺序创建一个不接受增删改的列表,源集合之后再追加元素也不会改变这个列表的结构。

官方地址:https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/util/List.html

要点速览
  • List.copyOf(source) 返回不可修改列表,调用 add、remove 或 set 会抛出 UnsupportedOperationException。
  • 输入集合或其中任意元素为 null 时,创建副本会抛出 NullPointerException。
  • 它主要隔离列表结构和源集合,不会递归复制元素对象;元素自身可变时,仍要单独设计对象边界。

List.copyOf 到底复制了什么

List.copyOf 接收的是 Collection extends E>,返回一个包含相同元素的 List。元素会按照源集合迭代器产生的顺序进入结果列表,所以源集合是 LinkedHashSet 或有明确顺序的列表时,顺序也会被保留下来。这里的“副本”首先指列表容器的独立状态,不等于对每个元素做深拷贝。

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

public class ListCopyOfDemo {
    public static void main(String[] args) {
        var source = new ArrayList();
        source.add("read"); // 先在可变源集合中准备权限
        source.add("write");

        var snapshot = List.copyOf(source); // 按迭代顺序创建不可变副本
        source.add("delete"); // 源集合继续变化,不会把新元素推入 snapshot

        System.out.println(source);   // [read, write, delete]
        System.out.println(snapshot); // [read, write]
    }
}

这个结果适合用在“调用方继续组装,接收方只读”的场景。例如控制器收到一组筛选字段后,可以在进入服务层前转成副本;服务层无需担心某个辅助方法偷偷调用 clear 或 add 改掉调用方的数据。

Java List.copyOf 从可变源集合创建不可变列表副本的静态结构图
图1:展示源集合、List.copyOf 与不可变列表之间的结构关系;这是静态说明图,不是运行截图。

副本不能改,元素对象却不一定不可变

Oracle API 对 List.of 和 List.copyOf 创建的列表都规定了不可修改、禁止 null 元素等特性。这里的不可修改是列表结构层面的约束:不能替换位置上的引用,也不能增加或移除元素。若元素是一个可变对象,仍然可以通过元素引用修改它的内部字段。

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

record MutableLabel(StringBuilder text) {}

public class ElementBoundaryDemo {
    public static void main(String[] args) {
        var label = new MutableLabel(new StringBuilder("draft"));
        var snapshot = List.copyOf(new ArrayList(List.of(label))); // 只固定列表结构

        label.text().append("-changed"); // 元素内部仍可变,副本能观察到同一个对象的变化
        System.out.println(snapshot.get(0).text()); // draft-changed

        // 列表位置不能替换;这行会抛出 UnsupportedOperationException。
        // snapshot.set(0, new MutableLabel(new StringBuilder("new")));
    }
}

因此,若业务需要真正的只读快照,元素类型也应使用不可变类型,例如字符串、不可变记录或创建了防御性副本的值对象。只把外层 List 换成 copyOf,不能自动保护嵌套的 Map、数组或自定义可变对象。

为什么 null 会让 List.copyOf 直接失败

List.copyOf 不接受空集合引用,也不接受含有 null 元素的集合。它在创建阶段就会拒绝这些输入,而不是生成一个以后再检查的特殊列表。这个行为对配置和权限列表很有用:空值问题会在边界处暴露,不会等到下游遍历时才产生含糊的错误。

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

public class NullInputDemo {
    static List freeze(List input) {
        if (input == null) {
            return List.of(); // 业务若允许“没有配置”,先明确转成空列表
        }
        return List.copyOf(input); // 输入含 null 时仍会抛出 NullPointerException
    }

    public static void main(String[] args) {
        var input = new ArrayList();
        input.add("safe");
        // input.add(null); // 不要把 null 静默混入需要冻结的配置
        System.out.println(freeze(input));
    }
}

是否把 null 当成空列表,应该由接口契约决定。上例的 freeze 只演示一种选择:方法参数本身为 null 时使用空列表;但集合内部出现 null 仍然保留失败,让调用方修复具体数据,而不是把缺失值悄悄变成字符串或默认权限。

Java List.copyOf 的结构不可变与元素可变边界静态关系图
图2:对比列表结构、源集合和元素对象三层边界,说明 copyOf 不等于深度复制;这是静态结构图,不是执行证据。

把 List.copyOf 放在什么接口边界

一个实用位置是“接收输入后立即固化”。调用方可以继续修改自己的 ArrayList,而被调用方保存的是当时的顺序和元素引用。返回值则可以直接暴露为 List,不必让调用者知道内部究竟用了哪种不可修改实现。

import java.util.List;

final class QueryPlan {
    private final List fields;

    QueryPlan(List fields) {
        this.fields = List.copyOf(fields); // 构造时固定列表结构,避免外部后续增删
    }

    List fields() {
        return fields; // 返回不可修改列表,调用方不能反向改变计划结构
    }
}

public class BoundaryDemo {
    public static void main(String[] args) {
        var fields = new java.util.ArrayList(List.of("id", "name"));
        var plan = new QueryPlan(fields);
        fields.add("created_at"); // 外部源集合变化不影响已构造的 plan
        System.out.println(plan.fields()); // [id, name]
    }
}

这个做法并不自动解决线程安全,也不代表元素内部没有共享引用。如果 fields 里放的是可变配置对象,仍然要在对象层做复制或改成不可变值类型。对普通字符串、枚举和不可变记录,List.copyOf 往往已经足够清晰。

选择 copyOf、unmodifiableList 还是可变副本

需求更合适的写法关键区别
创建结构不可变的列表副本List.copyOf(source)源集合后续结构变化不影响结果,拒绝 null 元素
只包一层只读视图Collections.unmodifiableList(source)源集合变化会反映到视图,仍共享同一个容器
需要继续增删改new ArrayList(source)获得可变容器,适合后续编辑
元素也要隔离复制元素后再组装列表需要按元素类型设计深拷贝或不可变值对象

最容易混淆的是 List.copyOf 与 Collections.unmodifiableList。前者创建自己的列表结构,后者只是给原列表加一层“不能通过这个引用修改”的视图。若原列表之后追加元素,视图可能看到新元素,而 copyOf 的结果不会跟着扩展。

使用前的四项检查

  • 确认业务要的是“结构不可变副本”,而不是会随源列表变化的只读视图。
  • 确认输入集合引用和每个元素都满足空值策略;需要允许 null 时,先转换成业务明确的默认值。
  • 确认元素本身是否可变;如果是嵌套集合、数组或可变 DTO,单层 copyOf 不足以做深度快照。
  • 确认所有修改操作都发生在复制前;副本创建后只把它当成读取接口使用。

两个容易混淆的问题

List.copyOf 会不会把源 ArrayList 的后续新增元素带过来?

不会。结果列表拥有独立的列表结构,创建完成后源集合再新增或移除元素,不会改变结果的元素数量和顺序。不过,若元素对象本身可变,结果中仍可能看到同一个对象的字段变化。

List.copyOf 和 Collections.unmodifiableList 哪个更安全?

要看边界。需要一次性的结构副本时选 List.copyOf;需要一个实时只读视图时才考虑 Collections.unmodifiableList。两者都不是深度复制,也都不能替代对可变元素和并发访问的设计。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Java LinkedHashMap 怎样用 accessOrder 实现最近最少使用淘汰Java LinkedHashMap 怎样用 accessOrder 实现最近最少使用淘汰
上一篇
Java LinkedHashMap 怎样用 accessOrder 实现最近最少使用淘汰
Java Stream mapMulti 怎样展开少量结果
下一篇
Java Stream mapMulti 怎样展开少量结果
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    523次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    580次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    607次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    557次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    377次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码