ArrayList知识点

一、ArrayList 核心定义

ArrayList 是 Java 集合框架中 List 接口的动态数组实现 (基于数组扩容),位于 java.util 包下,默认实现了 RandomAccess(支持随机访问)、Cloneable(可克隆)、Serializable(可序列化)接口,线程不安全

二、底层原理

  1. 存储结构 :底层基于Object [] 数组 (elementData)存储元素,JDK 7 中数组默认初始容量为 10(空参构造直接初始化 new Object[10]),JDK 8 做了优化(空参构造初始化空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA,首次添加元素时才扩容为 10,节省内存)。
  2. 扩容机制 (核心面试点):
    • 触发条件:当添加元素时,数组容量不足(size == elementData.length);
    • 扩容流程:
      1. 计算新容量:默认扩容为原容量的 1.5 倍(oldCapacity + (oldCapacity >> 1));
      2. 数组拷贝:通过 Arrays.copyOf() 将原数组元素拷贝到新数组(底层是 System.arraycopy () 本地方法);
    • 手动扩容:可通过 ensureCapacity(int minCapacity) 提前指定容量,减少多次扩容的性能损耗。

三、核心特性

表格

特性 说明
随机访问 支持通过索引快速访问(时间复杂度 O (1)),因为数组内存地址连续
增删效率 尾部增删:O (1)(无扩容时);非尾部增删:O (n)(需移动元素)
线程安全 线程不安全(未加同步锁),多线程场景需使用 Collections.synchronizedList()CopyOnWriteArrayList
允许 null 值 可存储 null 元素(可通过 indexOf (null) 查找 null 位置)
有序性 元素按插入顺序存储,索引从 0 开始
容量可动态调整 区别于普通数组(固定长度),ArrayList 可通过扩容自动调整容量

四、常用核心方法

java

运行

复制代码
// 1. 初始化
List<String> list1 = new ArrayList<>(); // JDK8 空数组,首次添加扩容为10
List<String> list2 = new ArrayList<>(20); // 指定初始容量20
List<String> list3 = new ArrayList<>(Arrays.asList("a", "b")); // 基于集合初始化

// 2. 增删改查
list1.add("java"); // 尾部添加(O(1))
list1.add(1, "python"); // 指定索引添加(O(n))
list1.remove(0); // 按索引删除(O(n))
list1.remove("java"); // 按元素删除(需遍历,O(n))
list1.set(0, "c++"); // 修改指定索引元素(O(1))
String elem = list1.get(0); // 随机访问(O(1))
int index = list1.indexOf("python"); // 查找元素索引(O(n))

// 3. 扩容相关
list1.ensureCapacity(100); // 手动指定最小容量,避免多次扩容
list1.trimToSize(); // 缩容:将数组容量调整为当前元素个数(释放多余内存)

// 4. 遍历(推荐方式)
// 方式1:for循环(随机访问效率高)
for (int i = 0; i < list1.size(); i++) {
    System.out.println(list1.get(i));
}
// 方式2:增强for循环(底层是迭代器)
for (String s : list1) {
    System.out.println(s);
}
// 方式3:迭代器(支持遍历中删除元素)
Iterator<String> it = list1.iterator();
while (it.hasNext()) {
    String s = it.next();
    if (s.equals("python")) {
        it.remove(); // 正确删除方式(避免ConcurrentModificationException)
    }
}

五、面试高频易错点

  1. 遍历中删除元素的坑

    • 错误:增强 for 循环中直接调用 list.remove() → 抛出 ConcurrentModificationException(快速失败机制,modCount != expectedModCount);
    • 正确:使用迭代器的 it.remove() 或普通 for 循环倒序删除。
  2. ArrayList vs LinkedList 区别 (高频对比):

    表格

    维度 ArrayList LinkedList
    底层结构 数组 双向链表(Node 节点)
    随机访问 O (1)(快) O (n)(慢,需遍历)
    增删效率 非尾部 O (n) 首尾增删 O (1),中间 O (n)
    内存占用 连续内存,有扩容冗余 每个节点存储前后指针,内存开销大
    适用场景 频繁查询、少量增删 频繁增删、少量查询
  3. JDK 7 vs JDK 8 初始化差异

    • JDK 7:空参构造 → 直接创建长度为 10 的数组;
    • JDK 8:空参构造 → 初始化空数组,首次 add 元素时才扩容为 10(懒加载)。
  4. subList () 陷阱

    • list.subList(from, to) 返回的是原数组的视图(非新数组),修改子列表会同步修改原列表;
    • 子列表持有原列表引用,原列表扩容后(数组地址变化),子列表操作会抛异常。

六、使用场景

  • 适合频繁查询、少量增删的场景(如数据展示、排行榜);
  • 不适合高频增删(非尾部)多线程并发修改的场景(后者优先用 CopyOnWriteArrayList)。

总结

ArrayList 核心关键点:

  1. 底层是动态扩容的 Object 数组,默认扩容 1.5 倍,随机访问效率高、非尾部增删效率低;
  2. 线程不安全,遍历删除需用迭代器,避免 ConcurrentModificationException;
  3. JDK 8 优化了初始化逻辑(懒加载),与 LinkedList 核心区别在底层结构和访问 / 增删效率。
相关推荐
皙然2 小时前
吃透 Java 泛型
java
斌糖雪梨2 小时前
invokeBeanFactoryPostProcessors(beanFactory); 方法详解
java·后端·spring
摇滚侠2 小时前
SpringBoot 工程,不是所有的服务都引入了 spring-boot-starter-amqp 依赖,我应该使用什么条件注解,判断配置类是否生效
java·spring boot·spring
码云数智-大飞2 小时前
解锁数据库极速引擎:索引底层机制、聚簇与非聚簇之争及性能避坑指南
开发语言
花间相见2 小时前
【JAVA基础03】—— JDK、JRE、JVM详解及原理
java·开发语言·jvm
FirstFrost --sy2 小时前
仿mudou库one thread one loop式并发服务器实现
运维·服务器·开发语言·c++
勿芮介2 小时前
【大模型应用】在window/linux上卸载OpenClaw
java·服务器·前端
kuntli2 小时前
Java内部类四种类型解析
java
xyq20242 小时前
Python 日期和时间处理指南
开发语言