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

4.ArrayList 扩容机制与 Fail-Fast 原理

ArrayList 扩容机制与 Fail-Fast 原理

🚀 高频指数:★★★★★
🎯 你将收获:ArrayList 内部结构、扩容策略、源码分析、fail-fast 机制与项目实践。


一、面试常见问法

💬 面试官:

  1. ArrayList 的默认容量是多少?
  2. 扩容时增长多少?
  3. 什么是 fail-fast?为什么会抛 ConcurrentModificationException?

这些问题考的是你对 集合源码与线程安全 的理解深度。


二、ArrayList 内部结构

ArrayList 是基于 动态数组实现 的顺序表,支持随机访问,插入删除效率低于 LinkedList。

🔹 源码定义(JDK 8)

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {transient Object[] elementData; // 存储元素private int size;               // 实际元素个数
}

底层是一个 Object[] 数组,随着元素增多自动扩容。


三、初始容量与构造方法

构造方式初始容量说明
new ArrayList()0(懒加载)第一次添加时初始化为10
new ArrayList(int initialCapacity)指定容量避免多次扩容
Arrays.asList()固定长度不支持 add/remove

JDK 7 以前默认初始化数组为 10;
JDK 8 之后延迟到第一次 add 时创建。


四、扩容机制详解

🔹 add() 方法核心流程

public boolean add(E e) {ensureCapacityInternal(size + 1); // 确保容量足够elementData[size++] = e;return true;
}

🔹 ensureCapacityInternal()

private void ensureCapacityInternal(int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {minCapacity = Math.max(10, minCapacity);}ensureExplicitCapacity(minCapacity);
}

🔹 真正扩容逻辑 grow()

private void grow(int minCapacity) {int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1); // 1.5倍扩容if (newCapacity < minCapacity)newCapacity = minCapacity;elementData = Arrays.copyOf(elementData, newCapacity);
}

结论:ArrayList 每次扩容 1.5 倍
旧数组拷贝到新数组(系统级复制,O(n))。


五、容量设计的取舍

策略优点缺点
每次 +1节省空间频繁扩容、低效
每次 ×2减少扩容次数空间浪费大
每次 ×1.5(ArrayList 采用)平衡时间与空间最优综合策略

📌 面试口诀:“空间换时间,一扩一半。”


六、fail-fast 原理

🔹 问题复现

List<String> list = new ArrayList<>();
list.add("A");
list.add("B");
for (String s : list) {if ("B".equals(s)) list.remove(s); // 抛异常
}

输出:

Exception in thread "main" java.util.ConcurrentModificationException

🔹 原理分析

ArrayList 内部维护一个 modCount(修改次数计数器)。

源码片段:

protected transient int modCount = 0;public E remove(int index) {rangeCheck(index);modCount++;// ...
}

迭代器初始化时,会记录一个期望值:

private class Itr implements Iterator<E> {int expectedModCount = modCount;
}

在遍历时校验:

final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();
}

🚩 核心机制:结构修改(增删元素)后 modCount 改变 → 触发异常。


七、解决 fail-fast 的三种方式

方案写法原理
✅ 使用 Iterator.remove()在迭代器内部安全删除同步更新 expectedModCount
✅ 使用 CopyOnWriteArrayList读写分离写时复制、线程安全
✅ 使用并发集合类ConcurrentLinkedQueue 等无结构修改异常

示例:

Iterator<String> it = list.iterator();
while (it.hasNext()) {if ("B".equals(it.next())) it.remove(); // ✅ 正确
}

八、面试官追问清单

问题答题要点
ArrayList 初始容量是多少?第一次添加时初始化为10
扩容策略是什么?每次扩容为原容量的1.5倍
为什么不是2倍?兼顾时间与空间效率
fail-fast 是什么?迭代时结构被修改触发并发修改异常
如何避免?使用 Iterator.remove 或并发集合

九、项目实践建议

  • 批量添加数据时先 ensureCapacity(size) 避免多次扩容;
  • 高并发场景用 CopyOnWriteArrayList
  • 频繁插入删除中间元素的场景使用 LinkedList
  • 扩容会拷贝数组,应避免在大数据循环中反复 add()

十、口诀记忆

☕️ “扩容一半,拷贝新家;遍历乱改,抛你异常。”


十一、小结

知识点要点
底层结构Object[] 动态数组
默认容量第一次添加时为10
扩容比例原容量 × 1.5
fail-fast 原理modCount 检测结构修改
解决方案Iterator.remove / 并发集合

✅ 一句话总结:
ArrayList 是动态数组,扩容靠复制,遍历要规矩。

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

相关文章:

  • 青岛网站域名备案玛酷机器人少儿编程加盟
  • 汽车OTA 测试用例
  • 常州网站建设流程阿里巴巴官网首页登录入口
  • 网站建设流程 知乎网站中文名称注册
  • P7: 《面试准备清单:如何高效覆盖90%的面试考点》
  • 27.短链系统
  • springboot+vue健康食谱交流平台设计(源码+文档+调试+基础修改+答疑)
  • 10.7 密码学中的线性代数
  • 【理论推导】互信息与InfoNCE损失:从公式推导理解对比学习的本质
  • 32HAL——万年历
  • 面向边缘智能的稳健医疗AI:模型性能衰减监控与自适应微调机制深度解析(上)
  • 专业网站发展趋势wordpress html模式
  • 最简单的手机网站制作最常用最齐全wordpress插件大全
  • 【Mybatis笔记】- 1 - MyBatis入门
  • Spring AI Alibaba 提示词入门:从零开始掌握AI对话技巧
  • AI 实战篇:用 LangGraph 串联 RAG+MCP Server,打造能直接操控 Jira 的智能体
  • 爱丽丝的人偶
  • 一个网站里面只放一个图片怎么做的交互式网站有哪些功能
  • 永川区门户网站建设轨迹开发流程管理
  • web中间件——Nginx
  • 读诗的时候我却使用了自己研发的Chrome元素截图插件
  • MyBatis框架如何处理字符串相等的判断条件
  • 搭建网站需要程序WordPress中文替换布
  • 【云运维】Python基础(一)
  • 自己动手造轮子:用Requests和线程池构建一个轻量级高并发爬虫框架
  • 养生网站策划成都行业网站建设那里好
  • 网站设计 韩国做一个模板网站多少钱
  • 【QT笔记】信号和槽
  • 农村电子商务网站建设wordpress4.9.4 安装
  • 【MATLAB第120期】基于MATLAB的SOBOL全局敏感性分析模型运用插件(含UI界面)