Java List 源码解析——从基础到深度剖析

Java List 源码解析------从基础到深度剖析

Java 集合框架中的 List 接口是开发中最常用的组件之一。无论是对数据的有序管理,还是对元素的高效访问,List 都发挥着重要作用。在这篇博客中,我们将从 List 的设计出发,逐步深入解析其主要实现类的源码,帮助你全面理解其工作原理和性能特点。


1. List 简介

1.1 什么是 List?

List 是 Java 集合框架(Java Collections Framework)中的一个接口,继承自 Collection 接口。它代表了一个有序的、可重复的元素集合。

例如:[A, B, C, A] 是一个合法的 List

1.2 List 的主要实现类

在实际开发中,List 有以下几个常用实现类:

  1. ArrayList:基于动态数组实现,适用于频繁随机访问的场景。
  2. LinkedList:基于双向链表实现,适用于频繁插入和删除的场景。
  3. CopyOnWriteArrayList:线程安全,适用于多读少写的并发场景。

2. List 接口设计

2.1 接口的定义

以下是 List 接口的核心定义,它扩展了 Collection 接口,增加了一些操作有序集合的功能:

java 复制代码
public interface List<E> extends Collection<E> {
    void add(int index, E element);
    E get(int index);
    E remove(int index);
    int indexOf(Object o);
    int lastIndexOf(Object o);
    List<E> subList(int fromIndex, int toIndex);
    // 其他方法省略
}

2.2 特点

  • 有序性List 会按照元素插入的顺序进行存储。
  • 允许重复List 允许存储重复的元素。
  • 随机访问:可以通过索引直接访问元素。

3. ArrayList 源码解析

ArrayList 是最常用的 List 实现类,其底层基于动态数组。以下是它的详细分析。

3.1 数据结构

ArrayList 使用一个动态数组(Object[] elementData)来存储元素。容量不足时,会进行扩容操作。

java 复制代码
// ArrayList 的核心属性
transient Object[] elementData;
private int size; // 当前 ArrayList 的大小

3.2 核心方法解析

add(E e) 方法

add() 方法是向列表中添加元素的核心方法。以下是其简化的源码:

java 复制代码
public boolean add(E e) {
    ensureCapacityInternal(size + 1); // 确保容量足够
    elementData[size++] = e;         // 添加元素
    return true;
}
  1. 容量检查

    调用 ensureCapacityInternal(size + 1) 方法,确保数组有足够的容量来存储新元素。如果容量不足,会触发扩容。

  2. 扩容机制 grow() 方法用于扩容,扩容逻辑如下:

    java 复制代码
    private void grow(int minCapacity) {
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容为原来的 1.5 倍
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
get(int index) 方法

通过索引访问元素,复杂度为 O(1):

java 复制代码
public E get(int index) {
    rangeCheck(index); // 检查索引是否越界
    return elementData[index];
}

4. LinkedList 源码解析

LinkedList 是基于双向链表实现的 List,更适合频繁的插入和删除操作。

4.1 数据结构

LinkedList 使用内部的 Node 类表示链表节点:

java 复制代码
private static class Node<E> {
    E item;       // 当前节点存储的元素
    Node<E> next; // 指向下一个节点
    Node<E> prev; // 指向前一个节点

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

4.2 核心方法解析

add(E e) 方法

add() 方法会在链表尾部插入新元素:

java 复制代码
public boolean add(E e) {
    linkLast(e); // 调用辅助方法
    return true;
}

void linkLast(E e) {
    final Node<E> l = last;                  // 保存当前链表的最后一个节点
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;                          // 更新最后一个节点
    if (l == null)                           // 如果链表为空
        first = newNode;                     // 更新第一个节点
    else
        l.next = newNode;                    // 否则更新前节点的 next
    size++;
}
get(int index) 方法

get() 方法通过遍历链表找到目标节点,复杂度为 O(n):

java 复制代码
Node<E> node(int index) {
    if (index < (size >> 1)) {
        Node<E> x = first; // 从头遍历
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
    } else {
        Node<E> x = last; // 从尾遍历
        for (int i = size - 1; i > index; i--)
            x = x.prev;
        return x;
    }
}

5. CopyOnWriteArrayList 源码解析

CopyOnWriteArrayList 是一种线程安全的 List,它通过写时复制(Copy-On-Write)实现线程安全。

5.1 数据结构

CopyOnWriteArrayList 的底层也是一个数组,但每次写操作都会复制整个数组。

5.2 核心方法解析

add(E e) 方法
java 复制代码
public boolean add(E e) {
    synchronized (lock) { // 加锁保证线程安全
        Object[] elements = getArray(); // 获取当前数组
        int len = elements.length;
        Object[] newElements = Arrays.copyOf(elements, len + 1); // 复制数组
        newElements[len] = e;
        setArray(newElements); // 替换原数组
        return true;
    }
}
优缺点
  • 优点:读操作无需加锁,读性能高。
  • 缺点:写操作的成本较高,尤其是数组很大时。

6. ArrayList vs LinkedList

特性 ArrayList LinkedList
底层实现 动态数组 双向链表
随机访问效率 高 (O(1)) 低 (O(n))
插入/删除效率 低 (O(n)) 高 (O(1))
内存使用 相对节省 占用更多

7. 总结与实践

  • 如果需要高效的随机访问,选择 ArrayList
  • 如果需要频繁插入或删除元素,选择 LinkedList
  • 在多读少写的并发场景中,选择 CopyOnWriteArrayList

希望通过本文,你对 List 接口及其实现类有了更深刻的理解。欢迎在实践中验证这些知识,并结合实际需求选择合适的实现类!

相关推荐
杰九1 分钟前
【全栈】SprintBoot+vue3迷你商城(2)
java·数据库·spring boot·mysql
华年源码2 分钟前
基于springboot的课程作业管理系统(源码+数据库+文档)
java·数据库·毕业设计·源码·springboot
黄霑和金庸我都喜欢7 分钟前
桌面开发 的设计模式(Design Patterns)核心知识
开发语言·后端·golang
Q_19284999061 小时前
基于Spring Boot的便民医疗服务小程序
spring boot·后端·小程序
开心工作室_kaic2 小时前
springboot548二手物品交易boot代码(论文+源码)_kaic
前端·数据库·vue.js·后端·html5
Java知识日历3 小时前
【内含例子代码】Spring框架的设计模式应用(第二集)
java·开发语言·后端·spring·设计模式
尘浮生5 小时前
Java项目实战II基于微信小程序的家庭大厨(开发文档+数据库+源码)
java·开发语言·数据库·微信小程序·小程序·maven
Java知识技术分享6 小时前
spring boot通过文件配置yaml里面的属性
java·spring boot·后端
Demons_kirit6 小时前
Spring Boot + Redis + Sa-Token
spring boot·redis·后端