Java 集合框架:ArrayList 的介绍、使用、原理与源码解析
大家好,我是栗筝i,这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 013 篇文章,在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验,并希望进一步完善自己对整个 Java 技术体系来充实自己的技术栈的同学。与此同时,本专栏的所有文章,也都会准备充足的代码示例和完善的知识点梳理,因此也十分适合零基础的小白和要准备工作面试的同学学习。当然,我也会在必要的时候进行相关技术深度的技术解读,相信即使是拥有多年 Java 开发经验的从业者和大佬们也会有所收获并找到乐趣。
–
本文将从介绍 ArrayList 开始,详细探讨其使用方法、工作原理以及背后的源码实现,帮助读者深入理解并灵活运用 ArrayList,以提升编程效率和代码质量。
在接下来的部分中,我们将首先概述 ArrayList 的基本特性及其在 Java 集合框架中的地位。随后,通过实际代码示例展示如何创建、操作和管理 ArrayList。接着,我们会揭示 ArrayList 的内部工作机制,包括其底层数据结构、扩容策略和性能优化等方面的内容。最后,我们将深入分析 ArrayList 的源码,探讨其设计思想和实现细节,以便读者能够更全面地掌握这一重要的集合类。
文章目录
- 1、ArrayList 概述
- 2、ArrayList 的具体实现原理
- 2.1、ArrayList 底层的数据结构
- 2.2、ArrayList 随机访问和按索引操作
- 2.3、ArrayList 的扩容机制
- 2.4、ArrayList 元素的删除
- 3、遍历 ArrayList 时移除元素时的问题及解决方案
- 3.1、在遍历 ArrayList 时移除一个元素时的问题
- 3.1.1、ConcurrentModificationException
- 3.1.2、跳过元素
- 3.2、在遍历 ArrayList 时移除一个元素时推荐的解决方案
- 3.2.1、使用迭代器的 remove 方法
- 3.2.2、使用 Java 8 的 removeIf 方法
- 3.2.3、使用逆向遍历
- 4、ArrayList 的使用(常用方法)
- 4.1、ArrayList 的常用方法
- 4.2、Collections 类中涉及 ArrayList 的常用方法
1、ArrayList 概述
ArrayList 是最常用的 List 实现类,内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要将已经有数组的数据复制到新的存储空间中。当从 ArrayList 的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高。因此,它适合随机查找和遍历,不适合插入和删除。
ArrayList 是基于数组实现的,相当于动态数组,其容量能动态增长,类似于 C 语言中的动态申请内存,动态增长内存。
ArrayList 的每个实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是大于等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造 ArrayList 时指定其容量。
ArrayList 在被添加大量元素前,应用程序可以使用 ensureCapacity() 操作来指定 ArrayList 实例的容量,这可以减少递增式再分配的数量。
ArrayList 是非线程安全的,只能在单线程环境下使用,多线程环境下可以考虑用 Collections.synchronizedList(List l) 函数返回一个线程安全的 ArrayList 类,也可以使用 java.util.concurrent 并发包下的 CopyOnWriteArrayList 类。
2、ArrayList 的具体实现原理
ArrayList 的原理其实很好概括和理解,简单概括就是:我们先在 ArrayList 中定义声明一个 Java 数组,接着定义对这个 Java 数组的,添加、删除、修改以及查询这四类方法。这其中最值得注意的是两点,一个是,由于底层操作的是数组,所以在进行删除操作是会涉及对大量元素的移动;另一个则是在增加元素并达到底层 Java 数组容量后的数组扩容问题。
2.1、ArrayList 底层的数据结构
ArrayList 在 Java 中是基于数组实现的动态数组,其核心数据结构是一个 Object 类型的数组 elementData,用于存储元素。
package java.util; import ... public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable { // 省略其他方法和实现细节 ... // 默认初始容量 private static final int DEFAULT_CAPACITY = 10; // 用于空实例的共享空数组实例 private static final Object[] EMPTY_ELEMENTDATA = {}; // 用于默认大小空实例的共享空数组实例 // 当添加第一个元素时,会扩展为 DEFAULT_CAPACITY private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 用于存储 ArrayList 元素的数组缓冲区 // transient 关键字表示该字段不会被序列化 transient Object[] elementData; // ArrayList 当前包含的元素数量 private int size; /` * 构造一个具有指定初始容量的空列表。 * * @param initialCapacity 列表的初始容量 * @throws IllegalArgumentException 如果指定的初始容量为负数 */ public ArrayList(int initialCapacity) { if (initialCapacity > 0) { this.elementData = new Object[initialCapacity]; // 初始化指定容量的数组 } else if (initialCapacity == 0) { this.elementData = EMPTY_ELEMENTDATA; // 使用共享的空数组实例 } else { throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); // 抛出非法参数异常 } } /` * 构造一个初始容量为 10 的空列表。 */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; // 使用默认大小的空数组实例 } /` * 构造一个包含指定集合元素的列表,这些元素按该集合的迭代器返回的顺序排列。 * * @param c 要放入列表中的集合 * @throws NullPointerException 如果指定的集合为 null */ public ArrayList(Collection c) - 功能:从列表中移除指定集合中也存在的所有元素。
- 用例:
ArrayList list = new ArrayList(Arrays.asList("a", "b", "c", "b")); list.removeAll(Collections.singleton("b")); // 移除所有 "b"- retainAll(Collection c)
- 功能:仅保留列表中那些也包含在指定集合中的元素。
- 用例:
ArrayList list = new ArrayList(Arrays.asList("a", "b", "c")); list.retainAll(Arrays.asList("a", "b")); // 保留 "a" 和 "b"- containsAll(Collection c)
- 功能:如果列表包含指定集合中的所有元素,则返回 true。
- 用例:
ArrayList list = new ArrayList(Arrays.asList("a", "b", "c")); boolean contains = list.containsAll(Arrays.asList("a", "b")); // 检查是否包含 "a" 和 "b"- clear()
- 功能:移除列表中的所有元素。
- 用例:
ArrayList list = new ArrayList(Arrays.asList("a", "b", "c")); list.clear(); // 清空列表4.2、Collections 类中涉及 ArrayList 的常用方法
- sort(List list)
- 功能:对列表进行排序(自然顺序)。
- 用例:
ArrayList list = new ArrayList(Arrays.asList(3, 1, 4, 1, 5)); Collections.sort(list); // 对列表排序
- reverse(List list)
-
功能:反转列表中元素的顺序。
-
用例:
ArrayList list = new ArrayList(Arrays.asList(1, 2, 3)); Collections.reverse(list); // 列表变为 [3, 2, 1]
- shuffle(List list)
- 功能:随机打乱列表中元素的顺序。
- 用例:
ArrayList list = new ArrayList(Arrays.asList(1, 2, 3)); Collections.shuffle(list); // 打乱列表元素的顺序
- binarySearch(List c, Object o)
- 功能:从集合中删除指定的元素。
- 用例:
ArrayList list = new ArrayList(Arrays.asList(1, 2, 3, 2, 1)); list.removeAll(Collections.singleton(1)); // 删除所有1
- copy(List c, Object o)
- 功能:返回指定元素在集合中出现的次数。
- 用例:
ArrayList list = new ArrayList(Arrays.asList(1, 2, 1, 3)); int freq = Collections.frequency(list, 1); // 计算1在列表中出现的次数
13, disjoint(Collection c1, Collection c2)
- 功能:如果两个集合没有共同的元素,则返回true。
- 用例:
ArrayList list1 = new ArrayList(Arrays.asList(1, 2, 3)); ArrayList list2 = new ArrayList(Arrays.asList(4, 5, 6)); boolean isDisjoint = Collections.disjoint(list1, list2); // 检查两个列表是否没有共同元素
