Arrays.asList() 的不可变陷阱:问题、原理与解决方案

🚨 Arrays.asList() 的不可变陷阱:问题、原理与解决方案

#Java集合 #开发陷阱 #源码解析 #编程技巧


一、问题现象:无法修改的集合

当开发者使用 Arrays.asList() 转换数组为集合时,尝试添加/删除元素会抛出异常:

String[] arr = {"Java", "Python", "Go"};  
List<String> list = Arrays.asList(arr);  // 尝试添加元素  
list.add("JavaScript"); // 抛出 UnsupportedOperationException  // 尝试删除元素  
list.remove(0); // 同样抛出异常  

控制台报错

Exception in thread "main" java.lang.UnsupportedOperationException  at java.util.AbstractList.add(AbstractList.java:148)  at java.util.AbstractList.add(AbstractList.java:108)  

二、原理剖析:为什么不可变?

2.1 源码分析

// Arrays.java  
public static <T> List<T> asList(T... a) {  return new ArrayList<>(a); // 注意:此ArrayList非java.util.ArrayList  
}  // Arrays内部的私有静态类  
private static class ArrayList<E> extends AbstractList<E>  implements RandomAccess, java.io.Serializable {  private final E[] a; // final修饰的数组!  ArrayList(E[] array) {  a = Objects.requireNonNull(array);  }  // 未重写add/remove方法(继承AbstractList的默认实现)  
}  // AbstractList.java  
public void add(int index, E element) {  throw new UnsupportedOperationException();  
}  

2.2 设计本质

特性Arrays.ArrayListjava.util.ArrayList
存储结构包装原始数组(final)动态数组(Object[] elementData)
长度是否可变❌ 固定长度✅ 动态扩容
是否支持增删❌ 抛出异常✅ 正常操作
内存占用更低(直接引用原数组)更高(拷贝数据)

关键限制

  • 底层数组由 final 修饰,无法扩容
  • 未重写 add()remove() 等修改方法
  • 继承 AbstractList 的默认实现(直接抛异常)

三、解决方案:创建真正的可变集合

3.1 使用 new ArrayList() 包装(推荐)

String[] arr = {"Java", "Python", "Go"};  // 方案1:构造方法包装  
List<String> mutableList = new ArrayList<>(Arrays.asList(arr));  // 方案2:Java 8+ Stream API  
List<String> mutableList = Arrays.stream(arr)  .collect(Collectors.toList());  

优点:代码简洁,兼容所有Java版本

3.2 Java 9+ 的 List.of() 替代方案

// 不可变集合(Java 9+)  
List<String> immutableList = List.of("Java", "Python", "Go");  // 需要可变时显式转换  
List<String> mutableList = new ArrayList<>(immutableList);  

注意List.of() 创建的集合完全不可变(增删改均抛异常)

3.3 特殊场景:修改原始数组

若只需修改元素值(不增删元素),可操作原始数组:

String[] arr = {"Java", "Python", "Go"};  
List<String> list = Arrays.asList(arr);  // 修改元素(允许!)  
list.set(1, "C++");  
System.out.println(Arrays.toString(arr)); // [Java, C++, Go]  // 原始数组同步变化  
arr[0] = "Rust";  
System.out.println(list); // [Rust, C++, Go]  

原理:集合直接引用原始数组,数据共享


四、最佳实践与总结

4.1 使用场景决策树

需要集合操作吗?  
├── 是 → 需要增删元素?  
│   ├── 是 → 使用 new ArrayList<>(Arrays.asList(...))  
│   └── 否 → 只需读/改元素 → Arrays.asList() 或 List.of()  
└── 否 → 直接使用原始数组  

4.2 各方案特性对比

方法可变性线程安全内存开销Java版本要求
Arrays.asList()部分❌非安全1.2+
new ArrayList<>(...)非安全1.2+
Arrays.stream().collect()非安全8+
List.of()安全9+

4.3 终极原则

  1. 明确需求:区分"只读" vs "可变"场景

  2. 优先新语法:Java 8+ 项目多用 Stream API

  3. 防御式编程

    // 返回不可修改视图(避免误操作)  
    public List<String> getLanguages() {  return Collections.unmodifiableList(Arrays.asList("Java", "Python"));  
    }  
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/910401.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/910401.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uniapp对接融云IM即时通讯,语音消息无法播放

uniapp对接融云IM即时通讯&#xff0c;语音消息无法播放 问题背景解决方案1.本地音频播放2.远程音频播放 问题背景 最近使用uniapp对接融云的即时通讯sdk&#xff0c;发送语音消息后&#xff0c;本地音频&#xff08;local&#xff09;和远程音频&#xff08;remote&#xff0…

【C++开发】CMake构建工具

目录 1&#xff0c;CMake介绍 2&#xff0c;配置文件CMakeLists.txt 1&#xff0c;CMake介绍 CMake 是一个开源的、跨平台的自动化构建系统生成工具&#xff0c;广泛用于 C 和 C 项目的构建管理。它使用一个名为 CMakeLists.txt 的配置文件来定义如何构建项目&#xff0c;并能…

大模型MetaGPT面试题汇总及参考答案

目录 MetaGPT 的核心目标与设计理念是什么? 它如何实现多角色协同(如 Planner、Coder、Reviewer、Tester)? 不同 agent 之间的通信机制是怎样的? MetaGPT 是如何进行任务拆分与任务分配的? 它如何实现可执行的反馈循环(self-correcting)? 在实际项目中如何监控各…

深入理解 HTTP 状态码 —— 前端后端必备知识

&#x1f4da;深入理解 HTTP 状态码 —— 前端后端必备知识 作者&#xff1a;lvzi 日期&#xff1a;2025 年 6 月 22 日 标签&#xff1a;HTTP、前端、后端、状态码、Web基础 &#x1f4a1;引言 在 Web 开发过程中&#xff0c;我们经常会遇到形如 200 OK、404 Not Found、500…

Python商务数据分析——Python 入门基础知识学习笔记

一、简介 1.1 Python 特性 解释型语言&#xff1a;代码无需编译可直接运行&#xff0c;适合快速开发。 动态类型&#xff1a;变量类型在运行时确定&#xff08;如x1后x"str"仍合法&#xff09;。 面向对象&#xff1a;支持类、对象、继承等特性&#xff0c;代码可…

IT小白到高手:HCIA、HCIP、HCIE认证攻略

大家好&#xff0c;这里是G-LAB IT实验室。6月&#xff12;&#xff12;日&#xff0c;周日&#xff01;HCIA&#xff0b;CCNA开新班啦&#xff01; 01 华为HCIA、HCIP、HCIE有必要考证吗 在如今竞争激烈的IT行业&#xff0c;华为的认证体系已成为众多网络工程师的重要参考。…

【IndexDB】前端IndexedDB终极指南

前端 IndexedDB 详细教程 IndexedDB 是一个浏览器内置的 NoSQL 数据库系统&#xff0c;允许在客户端存储大量结构化数据&#xff0c;并支持高性能搜索。相比 localStorage&#xff0c;IndexedDB 更适合存储大量数据并提供更复杂的查询功能。 基本概念 数据库&#xff1a;每个…

扩散模型与强化学习(1):字节Seedance中的人类偏好优化实践

扩散模型与强化学习(0)&#xff1a;专栏汇总与导航 前言&#xff1a;最近强化学习在Diffusion Models得到了越来越多广泛的应用&#xff0c;本专栏将系统性地介绍当前Diffusion Models中实用且前沿的技术进展。这篇博客介绍字节最新的视频生成模型Seedance 1.0: Exploring the …

【内存】Linux 内核优化实战 - vm.max_map_count

目录 vm.max_map_count参数全面解析一、参数定义与核心作用二、默认值与关键调整场景1. 默认限制与不足场景2. 典型报错案例 三、操作指南&#xff1a;查看与修改方法四、场景化建议值与配置示例五、关键注意事项六、延伸知识&#xff1a;内存映射的底层逻辑 vm.max_map_count参…

组件之间的双向绑定:v-model

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…

GetX 实现 MVVM 架构, 高效 路由管理 和 状态管理

GetX是Flutter中的一个高效的状态管理与路由管理框架&#xff0c;结合MVVM架构能简化代码逻辑。以下是使用GetX实现MVVM架构&#xff0c;并完成路由和状态管理的核心思路与实践&#xff1a; 一、MVVM架构在GetX中的映射 MVVM&#xff08;Model-View-ViewModel&#xff09;与G…

Qt项目,记事本

一、项目说明 项目功能&#xff1a; &#xff08;1&#xff09;打开文件&#xff1a;点击打开文件按钮弹出对话框&#xff0c;选择文本文件后&#xff0c;在主窗口编辑界面显示内容。 &#xff08;2&#xff09;关闭文件&#xff1a;关闭打开的文件&#xff0c;并询问是否保存…

【全开源】填表问卷统计预约打卡表单系统+uniapp前端

一.系统介绍 填表问卷统计预约打卡表单系统是ThinkPHPUniApp开发的一款集信息填表、预约报名&#xff0c;签到打卡、活动通知、报名投票、班级统计等功能的自定义表单统计小程序。 二.搭建环境 系统环境&#xff1a;CentOS、 运行环境&#xff1a;宝塔 Linux 网站环境&…

开源 python 应用 开发(一)python、pip、pyAutogui、python opencv安装

最近有个项目需要做视觉自动化处理的工具&#xff0c;最后选用的软件为python&#xff0c;刚好这个机会进行系统学习。短时间学习&#xff0c;需要快速开发&#xff0c;所以记录要点步骤&#xff0c;防止忘记。 链接&#xff1a; 开源 python 应用 开发&#xff08;一&#x…

SpringCloud + Zookeeper + Feign整合及Feign原理

知其然 SpringCloud Zookeeper Spring Cloud 与 Zookeeper的整合只需要添加相关的starter依赖和增加相关注解即可完成。 pom.xml 如下&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.…

深入探索 OpenCV 图像识别:从基础到深度学习

在当今数字化时代&#xff0c;图像识别技术已经渗透到我们生活的方方面面&#xff0c;从智能手机中的拍照翻译功能到自动驾驶汽车的目标检测系统&#xff0c;图像识别的应用无处不在。作为一名算法工程师&#xff0c;我有幸深入研究并实践了 OpenCV 在图像识别领域的强大功能。…

Hadoop部署(HA)高可用集群

一、准备工作 1.把集群全部停掉 在三台节点上都做&#xff08;在xshell通过右键----> 发送输入到--->所有会话&#xff09; 2..在/export/servers下创建HA目录 sudo mkdir -p /export/servers/HA 3.创建用户和设置所属主和所属组 #创建用户 sudo adduser ygre #设置…

STM32 CAN位同步、错误处理

一、接收方数据采样 CAN总线没有时钟线&#xff0c;总线上的所有设备通过约定波特率的方式确定每一个数据位的时长发送方以约定的位时长每隔固定时间输出一个数据位接收方以约定的位时长每隔固定时间采样总线的电平&#xff0c;输入一个数据位理想状态下&#xff0c;接收方能依…

django serializer __all__中 额外添加外键里的某一个属性

在Django中使用序列化器&#xff08;Serializer&#xff09;时&#xff0c;你可能会遇到需要将模型&#xff08;Model&#xff09;中的外键字段转换成其关联对象的一部分属性的情况。默认情况下&#xff0c;序列化器会自动序列化外键字段&#xff0c;但如果你想要在序列化结果中…

Redis快的原因

Redis 高性能的核心原因 Redis 之所以能达到极高的性能&#xff08;10万 QPS&#xff09;&#xff0c;主要源于以下几个关键设计&#xff1a; 1. 纯内存操作 核心优势&#xff1a;所有数据存储在内存中&#xff0c;避免了磁盘 I/O 瓶颈 内存访问速度比磁盘快 10万倍以上&am…