深入理解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,还能在面试中游刃有余。

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


参考文献

相关推荐
iOS开发上架哦19 分钟前
接口调试从入门到精通,Fiddler抓包工具、代理配置与HTTPS抓包实战技巧
后端
leonardee39 分钟前
Golang笔记——Interface类型
java·后端
武子康1 小时前
大数据-155 Apache Druid 存储与查询架构实战:Segment/Chunk/Roll-up/Bitmap 一文讲清
大数据·后端·nosql
张彦峰ZYF1 小时前
高并发优惠权益聚合接口的优雅实现(含超时控制 + 来源标识 + Fallback 降级)
java·后端·面试
聆听幸福1 小时前
Python判断语句
后端
yuuki2332331 小时前
【数据结构】常见时间复杂度以及空间复杂度
c语言·数据结构·后端·算法
程序员麻辣烫1 小时前
如何更换MySQL表的自增主键
后端
发仔1231 小时前
Java的Quartz定时任务引擎详解
java·后端
xyy1231 小时前
.NET Serilog
后端