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

List.subList() 返回值为什么不能强转成 ArrayList

List.subList() 返回值为什么不能强转成 ArrayList

先说结论

很多人用 subList() 的时候,可能会想当然地认为它返回的是一个新的 ArrayList。但实际上,subList() 返回的是原 List 的一个视图(View),并不是一个独立的 ArrayList 对象。

// 这样会报 ClassCastException
ArrayList<String> subArray = (ArrayList<String>) list.subList(0, 3); // 错误!

源码分析:subList 到底返回了什么?

我们先来看看 ArrayList 的 subList 方法源码:

public List<E> subList(int fromIndex, int toIndex) {subListRangeCheck(fromIndex, toIndex, size);return new SubList(this, 0, fromIndex, toIndex);
}

可以看到,它返回的是一个 SubList 对象,这是 ArrayList 的私有内部类:

private class SubList extends AbstractList<E> implements RandomAccess {private final AbstractList<E> parent;private final int parentOffset;private final int offset;int size;SubList(AbstractList<E> parent, int offset, int fromIndex, int toIndex) {this.parent = parent;this.parentOffset = fromIndex;this.offset = offset + fromIndex;this.size = toIndex - fromIndex;this.modCount = ArrayList.this.modCount;}public E get(int index) {rangeCheck(index);checkForComodification();return ArrayList.this.elementData(offset + index);}public E set(int index, E e) {rangeCheck(index);checkForComodification();E oldValue = ArrayList.this.elementData(offset + index);ArrayList.this.elementData[offset + index] = e;return oldValue;}
}

关键点:

  1. SubList 是 ArrayList 的私有内部类,继承 AbstractList,和 ArrayList 没有继承关系
  2. 它持有原 List 的引用(parent 字段)
  3. 它只记录了起始位置和长度(offsetsize),没有复制数据
  4. get/set 方法直接操作原 ArrayList 的底层数组 elementData
  5. 保存了创建时的 modCount,用于检测并发修改

所以 SubList 本质上就是一个视图,操作的是同一块内存区域。

基本用法

subList 方法签名:subList(fromIndex, toIndex),注意是左闭右开区间。

ArrayList<String> list = new ArrayList<>();
list.add("我");
list.add("爱");
list.add("J");
list.add("A");
list.add("V");
list.add("A");List<String> newList = list.subList(2, 6);
System.out.println("newList: " + newList);  // [J, A, V, A]
System.out.println("list: " + list);        // [我, 爱, J, A, V, A]

到这里看起来挺正常,但因为 newList 只是原 list 的视图,它们之间的操作会相互影响。根据修改方式的不同,分为两种情况:

非结构化修改

修改 subList,原 List 也会变:

List<String> newList = list.subList(2, 6);
newList.set(0, "A");  // 将 J 改为 ASystem.out.println("newList: " + newList);  // [A, A, V, A]
System.out.println("list: " + list);        // [我, 爱, A, A, V, A]

修改原 List,subList 也会变:

list.set(2, "O");
System.out.println("更新后的 list: " + list);        // [我, 爱, O, A, V, A]
System.out.println("更新后的 newList: " + newList);  // [O, A, V, A]

这就是视图的特性——它们指向同一块内存区域,结合前面的源码可以看到,get/set 方法操作的都是 ArrayList.this.elementData

结构化修改(重点!)

修改 subList 没问题:

List<String> newList = list.subList(2, 6);
newList.add(1, "O");  // 在 newList 中插入元素System.out.println("更新后的 list: " + list);        // [我, 爱, J, O, A, V, A]
System.out.println("更新后的 newList: " + newList);  // [J, O, A, V, A]

两个 List 都正常更新了,因为 SubList 的 add 最终调用的是原 ArrayList 的 add,并且会同步更新 modCount。

修改原 List 会抛异常:

List<String> newList = list.subList(2, 6);
list.add(1, "超");  // 修改原 list 的结构
System.out.println("更新后的 list: " + list);      // [我, 超, 爱, J, A, V, A]
System.out.println("更新后的 newList: " + newList);  // 💥 ConcurrentModificationException!

直接抛异常了!

为什么会抛异常?

来看 SubList 的校验代码:

private void checkForComodification() {if (ArrayList.this.modCount != this.modCount)throw new ConcurrentModificationException();
}

原理是这样的:

  1. SubList 创建时保存了当时的 modCount
  2. 原 ArrayList 每次结构化修改都会 modCount++
  3. SubList 的每个操作都会先调用 checkForComodification() 检查
  4. 如果发现原 ArrayList 的 modCount 和自己保存的不一致,直接抛异常

这就是 fail-fast(快速失败) 机制,用来尽早发现并发修改问题。

实际开发中怎么用?

1. 需要独立的 List

List<String> newList = new ArrayList<>(list.subList(2, 6));

用 ArrayList 的构造方法包一层,得到一个全新的独立 List,互不影响。

2. 利用视图特性

有时候就是想利用视图特性来修改原 List 的某一部分:

  • 通过 subList 修改 ✅
  • 持有 subList 时对原 List 做结构化修改 ❌

3. 一次性操作

java

list.subList(2, 6).clear();  // 清空原 list 的某个区间

用完即扔,不会有并发修改问题,而且效率很高。

http://www.dtcms.com/a/494380.html

相关文章:

  • phpcms网站转移网站关键词百度排名在下降
  • mac使用本地jdk启动elasticsearch解决elasticsearch启动时jdk损坏问题
  • 手机在初次联网的底层流程-关于EPC信令附着
  • 2025年红米手机上市了哪些款式,本别包含哪些版本,就上市时间、硬件参数、性能、价格等方面进行对比,加入横向竞品对比分析,按价位段划分推荐人群。
  • Go Web 编程快速入门 02 - 认识 net/http 与 Handler 接口
  • 成都网站建设网站制作济南网站制作哪家强
  • 广州做网站的网络公司网站建设美文
  • 云原生时代的数据库字段加密:在微服务与 Kubernetes 中实现合规与敏捷的统一
  • 虚拟机监控全攻略:从基础到云原生实战
  • fastgpt 社区版探究:mongo db 全文检索算法探秘
  • 防爆手机与普通手机有什么区别?防爆手机哪个牌子好?
  • 聊聊 Unity(小白专享、C# 小程序 之 日历、小闹钟)
  • 在vscode中全选后,同时在每行行尾,开始多行编辑(Mac版)
  • C4D域的重要修改层之延迟衰减和量化之解析
  • 建设银行网站网址是什么柳州电商网站建设
  • 记录WinFrom 使用 Autoupdater.NET.Official 进行软件升级更新,避免遗忘
  • 【汇编】RAX,eax,ax,ah,al 关系
  • 苍穹外卖 Day12 实战总结:Apache POI 实现 Excel 报表导出全流程解析
  • 网站分页符怎么做珠海网站建设哪个好薇
  • Redis的Docker安装
  • Windows 11 24H2 图形化安装 Docker Desktop(自定义安装路径到 D 盘)
  • python+uniapp基于微信小程序的瑜伽体验课预约系统
  • 什么是Bug呢?
  • 怎么制作网站记事本嘉兴网络科技有限公司
  • 外贸网站建设有用吗做外贸常用那几个网站
  • 【小白笔记】在 PyTorch 和 NumPy 这样的张量库中,形状(Shape) (3,) 的真正含义
  • 新版视频直播点播平台EasyDSS用视频破局,获客转化双提升
  • 【OS笔记07】:进程和线程5-进程的同步与互斥
  • 基于Session和Redis实现短信验证码登录
  • 视觉Slam14讲笔记第6讲非线性优化