List 经典问

List(JDK 1.8)

3.1 数组

3.1.1 数组概述

  • 数组定义:连续内存存储相同类型的数据的线性结构。

    int[] array = {22, 33, 88, 66, 55, 25};

  • 内存表示

    • 栈内存保存数组变量 array,存储数组首地址引用
    • 堆内存保存实际数组元素 [22,33,88,66,55,25]

3.1.2 数组寻址公式

访问数组元素:

复制代码
array[i] = baseAddress + i * dataTypeSize
  • baseAddress:数组首地址
  • i:索引
  • dataTypeSize:元素类型大小(int=4字节)

示例

复制代码
array[1] = 10 + 1 * 4 = 14

堆中地址 14 处就是 array[1] 的值 33。


3.1.3 操作时间复杂度

操作 时间复杂度 说明
随机访问 array[i] O(1) 直接计算地址
未知索引查找 O(n) 遍历数组
已排序数组二分查找 O(log n) 二分查找
插入 O(n) 插入位置后的元素都要后移
删除 O(n) 删除位置后的元素都要前移

3.2 ArrayList(JDK 1.8)源码分析

3.2.1 成员变量

复制代码
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object[] elementData; // 底层数组
private int size;               // 实际元素个数
  • DEFAULT_CAPACITY:默认初始容量 10,用于无参构造第一次 add 扩容
  • EMPTY_ELEMENTDATA :容量为 0 的共享空数组,用于 new ArrayList<>(0)
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA:用于无参构造的空数组,第一次 add 扩容到 10
  • elementData:实际存储元素的数组
  • size:当前元素个数

3.2.2 构造方法

复制代码
// 带初始容量
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);
    }
}

// 无参构造
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

// 通过 Collection 构造
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    size = elementData.length;
}
  • 无参构造 不会立即开 10 个长度数组,第一次 add 时才扩容
  • 带容量构造立即分配指定容量数组
  • Collection 构造 直接把 collection 转成数组赋值给 elementData,共享同一内存地址

3.2.3 添加数据流程 (

add(E e)

源码:

复制代码
public boolean add(E e) {
    ensureCapacityInternal(size + 1); // 确保容量
    elementData[size++] = e;           // 添加元素
    return true;
}
ensureCapacityInternal(size + 1)
复制代码
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
calculateCapacity
复制代码
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity); // 无参构造第一次 add 扩到 10
    }
    return minCapacity; // 其他情况直接返回 minCapacity
}
ensureExplicitCapacity
复制代码
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    if (minCapacity - elementData.length > 0) {
        grow(minCapacity); // 扩容
    }
}
grow 方法
复制代码
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩 1.5 倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity; // 不够用,直接用 minCapacity
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity); // 拷贝旧数组
}
  • 1.5 倍扩容,减少频繁扩容开销
  • Arrays.copyOf 复制旧元素到新数组
  • size++ 后添加新元素

3.2.4 初始容量 & 扩容总结

构造方式 初始 elementData.length 第一次 add 后容量
new ArrayList<>() 0 (DEFAULTCAPACITY_EMPTY_ELEMENTDATA) 10
new ArrayList<>(0) 0 (EMPTY_ELEMENTDATA) 1
new ArrayList<>(10) 10 不扩容
  • 扩容逻辑:容量不足 → 调 grow() → 新容量 = 旧容量 * 1.5(若不够用,则直接用 minCapacity)

3.2.5 面试题示例

  1. ArrayList list=new ArrayList(10):未添加元素,未扩容
  2. Arrays.asList(array)
    • 修改数组 → List 也变,底层共享同一内存
    • 修改 List 元素 → 数组也变
    • 不能 add/remove,固定长度
  3. list.toArray()
    • 返回数组是拷贝,修改 List 不影响数组,修改数组不影响 List

3.3 总结要点(面试必备)

  1. ArrayList 底层结构:动态数组(Object\[\])+ size
  2. 无参构造:第一次 add 才开容量 10
  3. 扩容策略 :1.5 倍,使用 Arrays.copyOf
  4. add(E e) 流程
    1. 计算所需容量 (size + 1) → calculateCapacity
    2. 确认容量 → ensureExplicitCapacity
    3. 扩容 → grow
    4. 添加元素 → elementData[size++] = e
  5. 数组与 List 转换
    • Arrays.asList → 底层共享数组
    • list.toArray() → 拷贝数组,不共享
  6. 数组操作复杂度
    • 随机访问 O(1)
    • 遍历查找 O(n)
    • 插入/删除 O(n)

相关推荐
西安邮电大学2 小时前
贪心算法详细讲解
java·后端·其他·算法·面试
qydz112 小时前
杰理开发板做TWS耳机类型方案分享(1)
开发语言·pcb工艺·嵌入式开发·杰理科技
慧都小妮子2 小时前
不想频繁改 PLC?用 DeviceXPlorer Lua 脚本把产线业务逻辑放到 OPC Server 层
java·junit·lua·takebishi·dxpserver·设备数据采集软件·opc server
迦蓝叶2 小时前
【开源自荐】JAiRouter:一个轻量级 AI 模型服务网关的开源实践
java·人工智能·spring·开源·llm-gateway·mass
Cloud_Shy6183 小时前
解读《Effective Python 3rd Edition》:从练气到老魔(第六章 Item 40 - 43)
android·开发语言·人工智能·笔记·python·学习方法
半只小闲鱼3 小时前
配置计划模块通用办公设备家具批复数合计计算
开发语言·python
swordbob3 小时前
缓存延迟双删的两种策略
java·缓存
凡人叶枫3 小时前
Effective C++ 条款08:别让异常逃离析构函数
java·linux·数据库·c++·嵌入式开发
云烟成雨TD3 小时前
Agent Scope Java 2.x 系列【4】模型层
java·人工智能·agent