当前位置: 首页 > news >正文

【Java踩坑笔记】14_Collections.singletonList的坑:不能add也不能set

摘要Collections.singletonList()返回的 List 是"只读"的,调用add()set()会抛UnsupportedOperationException。不止如此,它还有一些你不知道的性能特点。


一、问题现象

publicclassSingletonListTest{publicstaticvoidmain(String[]args){List<String>list=Collections.singletonList("hello");list.add("world");// ❌ UnsupportedOperationExceptionlist.set(0,"hi");// ❌ UnsupportedOperationExceptionlist.remove(0);// ❌ UnsupportedOperationExceptionSystem.out.println(list.get(0));// ✅ "hello"(get 可以)System.out.println(list.size());// ✅ 1(size 可以)}}

运行结果:

Exception in thread "main" java.lang.UnsupportedOperationException

不止singletonListArrays.asList()也有类似的坑。


二、踩坑现场

场景 1:把 singletonList 当普通 List 用

// ❌ 错误:以为 singletonList 返回的 List 是可修改的List<Order>orders=Collections.singletonList(newOrder(1L));orders.add(newOrder(2L));// ❌ UnsupportedOperationException

场景 2:Arrays.asList 的坑

// ❌ Arrays.asList 返回的 List 也不能 add/removeList<String>list=Arrays.asList("A","B","C");list.add("D");// ❌ UnsupportedOperationExceptionlist.remove(0);// ❌ UnsupportedOperationExceptionlist.set(0,"X");// ✅ 这个可以!(和 singletonList 不同)

关键区别Arrays.asList()的 List可以set(),但不能add()/remove()(长度固定)。
Collections.singletonList()的 Listset()也不行(完全只读)。

场景 3:作为参数传递时,调用方修改了 List

publicvoidprocess(List<String>tags){tags.add("processed");// ❌ 如果 tags 是 singletonList,这里会炸}

三、原理解析

3.1 Collections.SingletonList 的实现

// Collections.singletonList 源码publicstatic<T>List<T>singletonList(To){returnnewSingletonList<>(o);}// 内部类 SingletonListprivatestaticclassSingletonList<E>extendsAbstractList<E>implementsRandomAccess,Serializable{privatefinalEelement;SingletonList(Eobj){element=obj;}publicintsize(){return1;}publicbooleancontains(Objectobj){returneq(obj,element);}publicEget(intindex){if(index!=0)thrownewIndexOutOfBoundsException("Index: "+index);returnelement;}// 没有重写 add/set/remove → 继承 AbstractList 的默认实现// AbstractList 的 add/set/remove 直接抛 UnsupportedOperationException}

为什么set()也不支持?

SingletonList里的element是用final修饰的,且set()方法没有被重写(继承而来,直接抛异常)。

3.2 Arrays.asList 的坑

// Arrays.asList 返回的是 Arrays 的内部类 ArrayList(不是 java.util.ArrayList!)publicstatic<T>List<T>asList(T...a){returnnewArrayList<>(a);}// 这个 ArrayList 是 Arrays 的内部类privatestaticclassArrayList<E>extendsAbstractList<E>{privatefinalE[]a;// final 数组引用!@OverridepublicEset(intindex,Eelement){EoldValue=a[index];a[index]=element;// ✅ set 可以(修改数组元素)returnoldValue;}// 没有重写 add/remove → 抛 UnsupportedOperationException}

关键Arrays.ArrayList的底层数组引用是final的(不能换数组),但数组里的元素可以修改(所以set()可以)。

3.3 性能特点

Collections.singletonList()new ArrayList<>()更省内存:

new ArrayList<>(): - 创建一个 ArrayList 对象 - 创建一个 Object[] 数组(默认容量 10) - 内存开销:ArrayList 对象 + 数组头 + 10 个引用槽位 Collections.singletonList(): - 创建一个 SingletonList 对象 - 只存一个元素引用 - 内存开销:SingletonList 对象 + 1 个引用

结论:如果只需要一个元素的 List,用singletonList更省内存。


四、正确写法

4.1 需要可修改的 List:用 new ArrayList<>()

// ✅ 需要 add/removeList<String>list=newArrayList<>();list.add("hello");// ✅ 从 singletonList 转成可修改的 ListList<String>mutable=newArrayList<>(Collections.singletonList("hello"));mutable.add("world");// ✅

4.2 只需要只读 List:用 List.of()(Java 9+)

// ✅ Java 9+ 推荐用 List.of()List<String>list=List.of("hello");// 不可修改,比 singletonList 更通用List<String>list2=List.of("A","B","C");// 多个元素也支持

List.of()返回的是不可修改的 List,比Collections.unmodifiableList()更轻量。

4.3 需要不可修改但可能很大的 List:用 Collections.unmodifiableList()

// ✅ 把可修改的 List 包一层,变成只读视图List<String>mutable=newArrayList<>(Arrays.asList("A","B"));List<String>readOnly=Collections.unmodifiableList(mutable);readOnly.add("C");// ❌ UnsupportedOperationException// 但注意:如果修改了原 mutable List,readOnly 会看到变化(视图!)mutable.add("C");System.out.println(readOnly);// [A, B, C](看到了变化)

4.4 防御性编程:返回只读 List

// ✅ 方法返回时,如果不希望调用方修改,返回只读 ListpublicList<String>getTags(){List<String>tags=newArrayList<>();tags.add("java");tags.add("spring");returnCollections.unmodifiableList(tags);// 调用方不能修改}

五、最佳实践

✅ 选择 List 的 5 条规则

需求推荐方式是否可修改
只有一个元素List.of(e)/Collections.singletonList(e)❌ 不可修改
多个元素,不需要修改List.of(a, b, c)❌ 不可修改
需要 add/removenew ArrayList<>()✅ 可修改
需要 set,不需要 add/removeArrays.asList(...)⚠️ 可 set,不可 add/remove
返回只读视图Collections.unmodifiableList(list)❌ 不可修改(原 List 修改会影响它)

🔍 常见误区

误区:「Arrays.asList()返回的 List 不能修改」
更正:不能add/remove,但set()可以

误区:「Collections.unmodifiableList()返回的是副本」
更正:是视图,原 List 修改后,视图也能看到。

🛠️ Java 9+ 的 List.of() 优势

// Java 9+ 推荐用 List.of(),原因:// 1. 支持多个元素(不用先创建数组)// 2. 返回的 List 更省内存// 3. 不允许 null 元素(及早失败,避免 NPE)List<String>list=List.of("A","B","C");

六、小结

  • Collections.singletonList()返回完全只读的 List(add/set/remove全不支持)
  • Arrays.asList()返回的 List可以set(),但不能add()/remove()(长度固定)
  • List.of()(Java 9+)是更好的选择,支持多元素且不可修改
  • 需要可修改的 List,用new ArrayList<>()
  • Collections.unmodifiableList()返回的是视图,不是副本

下一篇预告:Stream 并行流不是银弹,用错反而更慢—— 并行流默认用ForkJoinPool.commonPool(),在什么场景下用,什么场景下反而会拖慢性能?

http://www.cnnetsun.cn/news/3074150.html

相关文章:

  • 2026年6月GESP真题及题解(C++一级):去旅行
  • pthread_create通过加锁设置线程启动竞争条件
  • 如何高效使用Diablo Edit2:暗黑破坏神2存档编辑器的完整指南
  • 查新报告分为哪几种?科技查新、查收查引与专利查新区别
  • Advanced XRay技术深度解析:如何通过方块渲染优化实现高效矿石定位
  • 5分钟免费让Windows拥有macOS优雅鼠标指针的完整指南
  • 用Backtrader回测DMI指标:一个Python量化新手的实战踩坑记录(附完整代码)
  • 基于sigrity的TDR/TDT仿真设计
  • 数据安全检查,这3个API盲区最容易被问穿
  • 如何用中文工作流轻松玩转AI绘画?这份完整指南让你从入门到精通
  • 第018章:ComfyUI文生图Z-Image模型创建数字人模特(二)
  • 01 静态分析(Static Analysis)
  • PKMS+AppOps 双权限体系:隐私管控、特权白名单全流程源码剖析
  • 2026年桌面风扇类型选购要点:从四个核心部件看懂一台风
  • Java实现字符串匹配:别再让算法理论画饼,实际应用才是王道
  • 把 ES Repository 纳入 CMS 轨道,一套更稳的 SAP PI 内容传输治理方式
  • Bebas Neue:开源字体设计的几何美学革命
  • 与你的 Elasticsearch 数据对话:使用 Google ADK 和 MCP 构建一个实时语音 agent ,分为 3 个组件
  • 从零理解 RAG:把“向量化“和“检索“讲成人话
  • 怎么用AI做历史课件视频?用 seedance2.0 制作趣味历史微课实战教程与对比
  • 机顶盒B860AV2.1-M刷机攻略
  • 高效XPath定位神器:xpath-helper-plus深度解析与实战指南
  • Java volatile 关键字相关用法总结:面试版详解
  • MYSQL--查询的执行流程
  • PC大型3A 角色扮演游戏(RPG)《怪物猎人物语3:命运双龙》网盘下载 免BIOS 中文版
  • 极低成本 AI 服务:独立开发者的多模型混合路由与流量网关设计
  • Python判断数字?别被isdigit()坑了!浮点负数全阵亡
  • UE5 插件版本 - PS添加PostProcess Pass
  • Beyond Compare 5永久激活:3步解决文件对比工具授权限制
  • Appium 移动端自动化环境搭建(Android/iOS)