news 2026/7/6 3:56:21

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【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(),在什么场景下用,什么场景下反而会拖慢性能?

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/4 18:40:58

2026年6月GESP真题及题解(C++一级):去旅行

2026年6月GESP真题及题解&#xff08;C一级&#xff09;&#xff1a;去旅行 题目描述 快暑假了&#xff0c;小杨同学正在计划出去旅行&#xff0c;前往目的地的方案多种多样&#xff0c;小杨同学想知道如何前往目的地最便宜。 小杨同学住在 AAA 市&#xff0c;旅行目的地是 B…

作者头像 李华
网站建设 2026/7/5 16:11:31

pthread_create通过加锁设置线程启动竞争条件

#include <pthread.h> template<class thd, class DATA> int Manage<thd, DATA>::attachthread(thd &t) {//设置系统级作用域和分离状态//设置系统级作用域 (system scope)含义&#xff1a;告诉操作系统&#xff0c;这个新创建的线程应该在整个系统范围…

作者头像 李华
网站建设 2026/7/4 16:27:14

如何高效使用Diablo Edit2:暗黑破坏神2存档编辑器的完整指南

如何高效使用Diablo Edit2&#xff1a;暗黑破坏神2存档编辑器的完整指南 【免费下载链接】diablo_edit Diablo II Character editor. 项目地址: https://gitcode.com/gh_mirrors/di/diablo_edit 你是否曾在暗黑破坏神2中花费数百小时刷装备&#xff0c;却因技能点分配失…

作者头像 李华
网站建设 2026/7/5 16:49:11

查新报告分为哪几种?科技查新、查收查引与专利查新区别

我朋友第一次开查新报告&#xff0c;跟我说直接看懵了…… 什么科技查新、查收查引、专利查新&#xff0c;分都分不清…… 特别担心选错了&#xff0c;还怕白花钱哈哈哈&#xff01; 今天这篇文章我就把这三种查新的区别和咋选机构的事一次讲清楚。 1.查新报告分为哪几种&am…

作者头像 李华
网站建设 2026/7/5 17:52:18

5分钟免费让Windows拥有macOS优雅鼠标指针的完整指南

5分钟免费让Windows拥有macOS优雅鼠标指针的完整指南 【免费下载链接】macOS-cursors-for-Windows Tested in Windows 10 & 11, 4K (125%, 150%, 200%). With 2 versions, 2 types and 3 different sizes! 项目地址: https://gitcode.com/gh_mirrors/ma/macOS-cursors-fo…

作者头像 李华