深入理解ArrayList:数据结构、源码分析与常见面试题

深入理解ArrayList:数据结构、源码分析与常见面试题

引言

ArrayList 是 Java 中最常用的集合类之一,它基于动态数组实现,提供了高效的随机访问和动态扩容能力。本文将深入探讨 ArrayList 的数据结构、源码实现以及常见的面试题,帮助你更好地理解和使用它。


一、数据结构:数组

数组的下标为什么从0开始?

数组的选址地址公式为:
baseAddr + i * typeSize

其中:

  • baseAddr 是数组的起始地址。
  • i 是数组下标。
  • typeSize 是每个元素占用的字节数。

如果数组下标从1开始,公式将变为:
baseAddr + (i - 1) * typeSize

这意味着 CPU 需要额外执行一个减法操作,增加了计算开销。而下标从0开始可以避免这种额外的计算,从而提高效率。


二、ArrayList源码分析

1. 成员变量

ArrayList 的核心成员变量如下:

java 复制代码
// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;

// 用于空实例的共享空数组
private static final Object[] EMPTY_ELEMENTDATA = {};

// 用于默认大小空实例的共享空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 存储ArrayList元素的数组缓冲区
transient Object[] elementData; // 非私有,以简化嵌套类访问

// ArrayList的大小(包含的元素数量)
private int size;

2. 构造方法

ArrayList 提供了三种构造方法:

(1) 指定初始容量
java 复制代码
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);
    }
}
(2) 默认构造方法(初始容量为10)
java 复制代码
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
(3) 通过集合初始化
java 复制代码
public ArrayList(Collection<? extends E> c) {
    Object[] a = c.toArray();
    if ((size = a.length) != 0) {
        if (c.getClass() == ArrayList.class) {
            elementData = a;
        } else {
            elementData = Arrays.copyOf(a, size, Object[].class);
        }
    } else {
        elementData = EMPTY_ELEMENTDATA;
    }
}

3. 添加与扩容机制

ArrayList 的添加操作涉及三种情况:

  1. 第一次添加元素:将容量从0扩容到默认值10。
  2. 第2到10次添加:直接添加元素,无需扩容。
  3. 第11次及以后添加:当容量不足时,扩容为原来的1.5倍,并进行数组拷贝。

扩容的核心逻辑在 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);
}

三、常见面试题

1. ArrayList底层实现的原理?

ArrayList 底层采用动态数组实现。初始化时容量为0,首次添加元素时容量会初始化为10。当容量不足时,ArrayList 会将容量扩容为原来的1.5倍,并通过数组拷贝将旧数据迁移到新数组中。

2. new ArrayList(10) 中数组扩容了几次?

new ArrayList(10) 只是实例化了一个对象,并指定初始容量为10,此时并未添加任何元素,因此不会触发扩容。

3. 如何实现数组与List之间的转换?

  • 数组转List :使用 Arrays.asList(数组对象)
  • List转数组 :使用 List.toArray(数组对象)

4. 转换后修改原内容,转换后的对象是否受影响?

  • 数组转List :受影响。因为 Arrays.asList 返回的 List 是基于原数组的视图,修改原数组会影响 List
  • List转数组 :不受影响。因为 toArray 方法会创建一个新的数组,与原 List 无关。

四、总结

ArrayList 是 Java 集合框架中非常重要的一个类,它的动态数组实现使其在随机访问和动态扩容方面表现出色。通过本文的分析,我们了解了 ArrayList 的核心实现原理、扩容机制以及常见的面试题。掌握这些知识,不仅可以帮助你更好地使用 ArrayList,还能在面试中游刃有余。

希望本文对你有所帮助!如果你有任何问题或建议,欢迎在评论区留言讨论。


参考文献

相关推荐
安清h几秒前
【基于SprintBoot+Mybatis+Mysql】电脑商城项目之修改密码和个人资料
数据库·后端·mysql·spring·mybatis
uhakadotcom34 分钟前
Java反序列化漏洞利用进阶:绕过WAF和EDR,实现隐蔽攻击
后端·架构·github
黑兔子1 小时前
Java|导出Excel文件
java·后端
二闹1 小时前
Java抽象工厂模式的面试题目及其答案
java·后端·面试
傲娇的萌1 小时前
mac彻底删除goland
后端
gopher_looklook1 小时前
深度讲解Go源码-sync.WaitGroup
后端·go·源码
uhakadotcom2 小时前
macOS 内核扩展 Fuzzing 指南:用户空间 + IDA + TinyInst
后端·架构·github
uhakadotcom3 小时前
震惊!Google的AI驱动的OSS-Fuzz工具在开源项目中发现大量漏洞!
后端·架构·github
枫叶落雨2223 小时前
14JavaWeb——SpringBoot原理
java·spring boot·后端
uhakadotcom3 小时前
利用Spring Boot 3.4.0属性实现远程代码执行的两种新方法
后端·面试·架构