java集合-ArrayList
·
ArrayList 是 Java 中最常用的动态数组集合类,位于 java.util 包下。它底层基于动态数组实现,支持自动扩容,解决了普通数组长度固定不可修改的痛点。
1.核心特点
- 动态扩容:容量不足时自动增长,无需手动指定长度
- 有序可重复:元素按插入顺序存储,允许重复元素、允许存
null - 随机访问快:通过索引直接访问元素,时间复杂度 O (1)
- 非线程安全:多线程环境下不推荐使用(推荐
CopyOnWriteArrayList) - 底层实现:
Object[]数组
2.基础使用
2.1 导包
import java.util.ArrayList;
2. 2 创建 ArrayList
// 1. 无参构造(默认初始容量10)
ArrayList<String> list1 = new ArrayList<>();
// 2. 指定初始容量(推荐,避免频繁扩容)
ArrayList<Integer> list2 = new ArrayList<>(20);
// 3. 泛型:指定存储的数据类型(只能存引用类型,基本类型用包装类)
// 错误:ArrayList<int> 正确:ArrayList<Integer>
2.3 常用方法
public class ArrayListDemo {
public static void main(String[] args) {
// 创建存储字符串的ArrayList
ArrayList<String> fruits = new ArrayList<>();
// 1. 添加元素 add()
fruits.add("苹果");
fruits.add("香蕉");
fruits.add(0, "草莓"); // 指定索引插入
// 2. 获取元素 get(索引)
System.out.println(fruits.get(0)); // 输出:草莓
// 3. 修改元素 set(索引, 新值)
fruits.set(1, "芒果");
// 4. 删除元素 remove(索引) / remove(元素)
fruits.remove(2); // 删除索引2的元素
fruits.remove("芒果"); // 删除指定元素
// 5. 获取长度 size()
System.out.println("元素个数:" + fruits.size());
// 6. 判断是否包含 contains()
System.out.println(fruits.contains("苹果"));
// 7. 清空集合 clear()
// fruits.clear();
// 8. 判断是否为空 isEmpty()
System.out.println(fruits.isEmpty());
}
}
2.4 遍历 ArrayList
1.普通 for 循环(通过索引)
for (int i = 0; i < fruits.size(); i++) {
System.out.println(fruits.get(i));
}
2.增强 for 循环(推荐,简洁)
for (String fruit : fruits) {
System.out.println(fruit);
}
3.迭代器 Iterator
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
3. ArrayList 自动扩容机制
- 无参创建时,初始为空数组,第一次添加元素时扩容为
10 - 容量满了之后,自动扩容为原容量的 1.5 倍
- 扩容本质:创建新数组 → 复制旧元素 → 替换旧数组
- 优化建议:提前指定初始容量,减少扩容次数
4.ArrayList vs 普通数组
| 特性 | 普通数组 | ArrayList |
|---|---|---|
| 长度 | 固定不可变 | 动态自动扩容 |
| 存储类型 | 基本类型 + 引用类型 | 仅引用类型(需包装类) |
| 功能 | 简单,无内置方法 | 方法丰富(增删改查) |
| 使用场景 | 固定长度数据 | 长度不确定、频繁操作 |
5.ArrayList的实现(源码)
5.1 底层数据结构
// 1. 默认初始容量
private static final int DEFAULT_CAPACITY = 10;
// 2. 空实例共享的空数组(无参构造用)
private static final Object[] EMPTY_ELEMENTDATA = {};
// 3. 无参构造专用空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 4. 真正存储元素的底层数组【核心】
transient Object[] elementData;
// 5. 实际元素个数(不是数组容量)
private int size;
// 6. 最大数组容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
1. DEFAULT_CAPACITY
private static final int DEFAULT_CAPACITY = 10;
- 含义:默认初始容量
- 作用:无参构造创建 ArrayList 后,第一次 add 元素时,底层数组会初始化为长度 10。
- 不是一 new 就创建长度 10 数组,懒加载。
2. EMPTY_ELEMENTDATA
private static final Object[] EMPTY_ELEMENTDATA = {};
- 空常量数组,有参构造传入容量为 0 时使用。
- 例如:
new ArrayList<>(0)底层赋值为这个空数组。
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 为0时赋值
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
3. DEFAULTCAPACITY_EMPTY_ELEMENTDATA
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
- 无参构造
new ArrayList<>()默认赋值这个空数组 - 作用:和上面空数组做区分,用来标记「是无参构造创建的」,方便第一次 add 时直接扩容到 10。
- 两个空数组都是空数组,只是标记身份不同,为了后续扩容逻辑区分
4. elementData 【最核心】
transient Object[] elementData;
- 底层真正存放所有元素的数组
transient:序列化时不直接序列化整个数组,只序列化有效元素,节省空间。- 泛型
<E>只是语法糖,底层全部存Object,取元素时强转。
5. size
private int size;
- 实际已存储的元素个数
- 区别:
capacity:底层数组elementData的总容量size:真实元素数量
- 永远满足:
size ≤ 数组容量
6. MAX_ARRAY_SIZE
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
- 允许分配的最大数组大小
- 减 8 是为了规避部分 JVM 数组头内存溢出问题。
- 超过这个大小就直接用
Integer.MAX_VALUE。
5.2 构造函数
1. 无参构造
public ArrayList() {
elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
- 刚创建:底层是空数组,容量 0
- 第一次 add:自动扩容到
DEFAULT_CAPACITY = 10
2.指定初始容量构造
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException(...);
}
}
- 容量 > 0:直接创建对应长度 Object 数组
- 容量 = 0:赋值
EMPTY_ELEMENTDATA
3.集合入参构造
public ArrayList(Collection<? extends E> c) {
// 1. 把传入的集合转成数组,赋值给 elementData
elementData = c.toArray();
// 2. 把数组长度赋值给 size,然后判断长度是否为 0
if ((size = elementData.length) != 0) {
// 重点:JDK 官方注释:c.toArray() 可能错误地不返回 Object[]
// 3. 如果数组类型不是 Object[],就强制拷贝成 Object[]
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 4. 传入空集合,直接用空数组常量
this.elementData = EMPTY_ELEMENTDATA;
}
}
把传入集合转数组,赋值给 elementData,并给 size 赋值。
更多推荐
所有评论(0)