1.2 ArrayList 源码解析

1.2.1 底层结构

底层transient Object[] elementData动态数组,实现RandomAccess标记接口,标记支持随机访问,for 循环遍历性能远高于迭代器。核心字段:

java 复制代码
private static final int DEFAULT_CAPACITY = 10; // 默认初始容量
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 无参构造空数组
private static final Object[] EMPTY_ELEMENTDATA = {}; // 指定0容量空数组
transient Object[] elementData; // 存储元素数组,transient不序列化
private int size; // 当前存储元素数量
protected transient int modCount; // 修改计数,快速失败

文字解释

  • 无参构造不会直接创建长度 10 数组,延迟初始化,首次 add 才扩容,节省内存;
  • transient:序列化时跳过数组,writeObject 手动序列化元素,过滤空元素节省空间。

1.2.2 三大构造方法注释

arduino 复制代码
// 无参构造,赋值空延迟数组,首次add扩容至10
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

// 指定初始容量
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        // 大于0创建对应长度数组
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        // 0容量赋值空数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        // 负数抛非法参数异常
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    }
}

// 传入集合初始化ArrayList
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // 部分集合toArray返回非Object数组,强制转换
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // 传入空集合,赋值空数组
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

1.2.3 add () 扩容完整源码注释

scss 复制代码
public boolean add(E e) {
    // 校验容量,不足则扩容
    ensureCapacityInternal(size + 1);
    elementData[size++] = e; // 尾部存入元素,size自增
    return true;
}

// 计算所需最小容量
private void ensureCapacityInternal(int minCapacity) {
    // 判断是否是无参构造的空数组,首次add最小容量取10
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++; // 修改计数+1
    // 所需容量超过数组长度,执行扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

// 扩容核心方法
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容1.5倍
    // 新容量仍不足,直接使用最小需要容量
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 超过最大数组长度,赋值最大限制
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 数组拷贝,开辟新内存,复制旧数组全部元素
    elementData = Arrays.copyOf(elementData, newCapacity);
}

文字扩容解释

  1. 无参 ArrayList 是空数组,第一次 add 扩容至 10;
  2. 每次扩容原数组 1.5 倍,通过Arrays.copyOf复制全部元素,拷贝开销大;
  3. 大批量 add 前调用list.ensureCapacity(预估数量),减少多次拷贝。

1.2.4 各操作时间复杂度

  1. get (index) /set (index,E):O (1) 随机访问,数组下标直接寻址;
  2. 尾部 add:均摊 O (1),仅扩容时拷贝;
  3. 头部 / 中间 add (int)/remove (int):O (n),数组整体移位复制。

1.2.5 fail-fast 快速失败机制

增删时 modCount 自增,迭代器创建保存expectedModCount,遍历对比两者不一致直接抛出ConcurrentModificationException,仅用于检测 bug,不能并发同步。

1.2.6 线程安全问题

无同步锁,多线程读写会数组越界、数据丢失;安全方案:List list = Collections.synchronizedList(new ArrayList<>())

相关推荐
用户41659673693551 小时前
Android WebView 加载 file:// 离线页面调试教程
android·前端
Asmewill1 小时前
curl命令学习笔记一
前端
星栈1 小时前
我用 Rust + Dioxus 做了个全栈跨平台笔记应用:再把新建、编辑和交付补上
前端·rust·前端框架
我是一只快乐的小螃蟹1 小时前
1.1 HashMap (JDK1.8) 源码解析
前端
爱勇宝4 小时前
小红花成长新版:模板来了,鼓励也更容易开始
前端·后端·程序员
竹林8184 小时前
Solana前端开发:我在一个NFT铸造页面上被@solana/web3.js的Connection和Transaction签名坑了两天
前端
冬奇Lab5 小时前
每日一个开源项目(第144篇):ai-website-cloner-template - 一条命令、多 Agent 并行,把任意网站逆向成 Next.js 代码
前端·人工智能·开源
玄玄子5 小时前
webpack publicPath作用原理
前端·webpack·程序员
HduSy5 小时前
帮 Claude Code 做了个菜单栏 Token 看板,聊聊里面的一些实现逻辑
前端