Java ArrayList 完整详解

Java ArrayList 完整详解

一、概述

ArrayListjava.util 包下基于动态数组实现的 List 集合,底层是 Object\[\] 数组,自动扩容,支持随机访问、增删改查。

  • 线程不安全 ,多线程并发修改会抛 ConcurrentModificationException
  • 允许存放 null,元素可重复、有序(插入顺序不变)
  • 查询快、中间/头部删除插入慢(需要数组移位)

类关系

java 复制代码
ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, Serializable

RandomAccess 标记接口:支持下标快速随机访问,遍历优先用普通for循环。

二、底层原理

  1. 默认初始容量DEFAULT_CAPACITY = 10
  2. 空构造器先赋值空数组,第一次add时才扩容到10,节省内存
  3. 扩容机制
    • 新容量 = 旧容量 + 旧容量 / 2(1.5倍扩容)
    • 若1.5倍仍不够,直接取所需最小容量
    • 最大容量:Integer.MAX_VALUE - 8
  4. 增删操作:数组复制 Arrays.copyOf(),移位消耗性能

三、常用构造方法

java 复制代码
// 1. 空构造,初始数组为空,首次add扩容至10
ArrayList<String> list1 = new ArrayList<>();

// 2. 指定初始容量(推荐已知数据量时使用,减少扩容)
ArrayList<Integer> list2 = new ArrayList<>(20);

// 3. 传入集合,复制所有元素
List<Integer> temp = Arrays.asList(1,2,3);
ArrayList<Integer> list3 = new ArrayList<>(temp);

四、核心常用API

1. 添加元素

java 复制代码
ArrayList<String> list = new ArrayList<>();
list.add("A");                // 尾部添加
list.add(1, "B");             // 指定下标插入(下标不能越界)
list.addAll(Arrays.asList("C","D")); // 批量添加集合

2. 获取元素

java 复制代码
String s = list.get(0);       // 根据下标取值,越界抛IndexOutOfBoundsException
int size = list.size();       // 获取集合元素个数

3. 修改元素

java 复制代码
list.set(0, "AA"); // 替换指定下标元素,返回旧值

4. 删除元素

java 复制代码
list.remove(0);        // 按下标删除,返回被删元素
list.remove("B");      // 按对象删除,只删第一个匹配元素,返回boolean
list.removeAll(Arrays.asList("C","D")); // 删除交集元素
list.clear();          // 清空所有元素,数组保留容量

5. 判断与查找

java 复制代码
list.contains("A");    // 是否包含元素
list.indexOf("A");     // 首个匹配下标,无则-1
list.lastIndexOf("A"); // 最后一个匹配下标
list.isEmpty();        // 是否为空集合

6. 转换数组

java 复制代码
Object[] arr1 = list.toArray();
String[] arr2 = list.toArray(new String[0]); // 转指定类型数组

五、三种遍历方式

1. 普通for(推荐,随机访问最快)

java 复制代码
for (int i = 0; i < list.size(); i++) {
    System.out.println(list.get(i));
}

2. 增强for循环(foreach,底层迭代器)

java 复制代码
for (String str : list) {
    System.out.println(str);
}

3. Iterator迭代器(遍历中安全删除)

java 复制代码
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    String s = it.next();
    if ("A".equals(s)) {
        it.remove(); // 安全删除,不会并发修改异常
    }
}

❌ 禁止foreach里直接 list.remove(),会触发并发修改异常。

六、线程安全问题

ArrayList 非线程安全,多线程同时读写会报错或数据错乱。

解决方案

  1. Vector:旧集合,方法全加synchronized,性能差,不推荐
  2. Collections.synchronizedList():包装成同步集合
java 复制代码
List<String> safeList = Collections.synchronizedList(new ArrayList<>());
  1. CopyOnWriteArrayList:读写分离,读不加锁,写复制新数组,读多写少场景首选

七、常用示例代码

java 复制代码
import java.util.ArrayList;
import java.util.Arrays;

public class ArrayListDemo {
    public static void main(String[] args) {
        ArrayList<Integer> nums = new ArrayList<>();
        // 添加
        nums.add(10);
        nums.add(20);
        nums.add(30);
        nums.add(1, 15);

        // 查询
        System.out.println(nums.get(1)); // 15
        System.out.println(nums.size()); // 4

        // 修改
        nums.set(0, 99);

        // 删除
        nums.remove(Integer.valueOf(30));

        // 遍历
        for (Integer num : nums) {
            System.out.print(num + " ");
        }
    }
}

八、ArrayList vs LinkedList

特性 ArrayList LinkedList
底层 动态数组 双向链表
查询 快 O(1) 慢 O(n)
头尾增删 尾部快,头部慢 头尾极快 O(1)
内存 连续数组,占用少 每个节点存前后指针,开销大
适用场景 大量查询、少量删除 频繁头部/中间插入删除

九、常见坑

  1. 下标越界get(i)add(i,obj) i >= size 抛异常
  2. foreach遍历中调用list.remove() → ConcurrentModificationException,要用迭代器删除
  3. Arrays.asList() 返回的集合不能add/remove,底层固定数组,需套new ArrayList<>()
  4. 存储基础类型会自动装箱成包装类(int→Integer)
  5. 扩容会产生大量数组复制,大数据初始化建议指定容量