一行一行读Java源码——ArrayList

简介: ArrayList可能是日常开发中使用频率最高的集合类型之一(另一个是HashMap),但是有些细节不细究、不常回顾的话总是会慢慢模糊,比如有一语句`List objects = new ArrayList();` ,试问此时`objects` 的容量(capacity)是多大?

ArrayList可能是日常开发中使用频率最高的集合类型之一(另一个是HashMap),但是有些细节不细究、不常回顾的话总是会慢慢模糊,比如有一语句List<Object> objects = new ArrayList<>(); ,试问此时objects 的容量(capacity)是多大?
尽管类似这样的细节对代码质量的影响可能不大,但作为Java程序员我们还是有必要去熟悉这些细节并挖掘隐含在这些细节背后的设计奥秘。鉴于此,我觉得有必要再次细度Java ArrayList源码。
首先列举一下本文所涉及的知识点:
1、ArrayList内部如何实现?适合什么样的操作场景?
2、new ArrayList<>()方法调用后所提供的ArrayList容量是多大?
3、未提供容量值,但是调用add方法后ArrayList容量值是多大?
4、ArrayList什么时候扩容?如何扩容?扩多大?
5、ArrayList是否线程安全?
6、ArrayList如何序列化?
7、ArrayList最大容量是多大?

别废话,请开始你的表演

// 类的定义,ArrayList继承自AbstractList
// AbstractList中声明了List该有的一些方法,同时实现了迭代器
// ArrayList具有随机访问、克隆以及序列化的能力
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable,java.io.Serializable

ArrayList的一些静态变量

// ArrayList的默认的容量大小是10
private static final int DEFAULT_CAPACITY = 10;

// 空list
private static final Object[] EMPTY_ELEMENTDATA = {};

// 创建ArrayList实例时,如果未提供capacity,那ArrayList内部将会是这个数组,其capacity是0,然后在add第一个元素的时候进行扩容。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

ArrayList内部实现

// ArrayList内部实现就是这个数组,其length就是ArrayList的capacity
// transient关键字意味着elementData将不会序列化,那么ArrayList又将如何序列化?
transient Object[] elementData;

数组是顺序表,这意味着ArrayList不适合在非尾部插入和删除

new ArrayList<>()方法

// 这里说明new ArrayList<>()时,ArrayList是空的,capacity为0
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

再来看add方法

// add首先会就行容量校验
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

ensureCapacityInternal方法

// 当elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA时,会将list的capacity设置为Math.max(DEFAULT_CAPACITY, minCapacity),所以capacity至少会是10
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity)
    }

    ensureExplicitCapacity(minCapacity);
}

ArrayList最大容量

// ArrayList的最大容量是Integer.MAX_VALUE - 8,减8的原因是因为一些虚拟机在数组中有预留位保存头部信息
/**
 * The maximum size of array to allocate.
 * Some VMs reserve some header words in an array.
 * Attempts to allocate larger arrays may result in
 * OutOfMemoryError: Requested array size exceeds VM limit
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

ArrayList扩容

// 至少会扩容 1/2 oldCapacity(向下取整)
// 如果newCapacity小于最少需要的容量minCapacity,那newCapacity=minCapacity
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

// 取MAX_ARRAY_SIZE或者抛出OutOfMemoryError异常
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

// 该方法中调用了grow方法,即当前需要的容量要比当前ArrayList的capacity大时进行扩容
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

ArrayList序列化

为什么ArrayList会采用这两个方法来完成序列化与反序列化,见Java集合序列化

private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

    /**
     * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
     * deserialize it).
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;

        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in capacity
        s.readInt(); // ignored

        if (size > 0) {
            // be like clone(), allocate array based upon size not capacity
            ensureCapacityInternal(size);

            Object[] a = elementData;
            // Read in all elements in the proper order.
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }

总结

1、ArrayList内部如何实现?适合什么样的操作场景?
数组实现,适合随机存取、不适合非尾部的增删操作。

2、new ArrayList<>()方法调用后所提供的ArrayList容量是多大?
0

3、未提供容量值,但是调用add方法后ArrayList容量值是多大?
至少是10,或者实际需要值(大于10)

4、ArrayList什么时候扩容?如何扩容?扩多大?
当前需要的容量要比当前ArrayList的capacity大时进行扩容;扩容的操作是重新分配数组;至少会扩容 1/2 oldCapacity(向下取整),如果newCapacity小于最少需要的容量minCapacity,那将扩大至最少需要容量。

5、ArrayList是否线程安全?
不是,没有任何synchronized方法。

6、ArrayList如何序列化?
通过readObject和writeObject,详见集合序列化

7、ArrayList最大容量是多大?
Integer.MAX_VALUE - 8,部分虚拟机在数组中预留了8位存储头部信息。

目录
相关文章
|
5月前
|
人工智能 安全 JavaScript
Java ArrayList:动态数组
本文探讨Java中的数组,对比C/C++、JS/PHP/Python等语言的数组特性。文章分析了Java数组的定义、创建方式及其规范,指出其优缺点。Java数组作为引用类型,在堆上分配内存,支持动态大小,避免了C/C++中裸数组的常见问题(如越界访问)。然而,Java数组也存在性能瓶颈和设计缺陷,例如运行时的安全检查影响速度,无法创建超大数组或泛型数组,且多线程场景下缺乏同步机制。作者建议在实际开发中用集合替代数组以规避这些问题。
123 1
|
6月前
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
451 7
|
6月前
|
消息中间件 算法 安全
JUC并发—1.Java集合包底层源码剖析
本文主要对JDK中的集合包源码进行了剖析。
|
6月前
|
人工智能 安全 Java
智慧工地源码,Java语言开发,微服务架构,支持分布式和集群部署,多端覆盖
智慧工地是“互联网+建筑工地”的创新模式,基于物联网、移动互联网、BIM、大数据、人工智能等技术,实现对施工现场人员、设备、材料、安全等环节的智能化管理。其解决方案涵盖数据大屏、移动APP和PC管理端,采用高性能Java微服务架构,支持分布式与集群部署,结合Redis、消息队列等技术确保系统稳定高效。通过大数据驱动决策、物联网实时监测预警及AI智能视频监控,消除数据孤岛,提升项目可控性与安全性。智慧工地提供专家级远程管理服务,助力施工质量和安全管理升级,同时依托可扩展平台、多端应用和丰富设备接口,满足多样化需求,推动建筑行业数字化转型。
225 5
|
1月前
|
存储 小程序 Java
热门小程序源码合集:微信抖音小程序源码支持PHP/Java/uni-app完整项目实践指南
小程序已成为企业获客与开发者创业的重要载体。本文详解PHP、Java、uni-app三大技术栈在电商、工具、服务类小程序中的源码应用,提供从开发到部署的全流程指南,并分享选型避坑与商业化落地策略,助力开发者高效构建稳定可扩展项目。
|
3月前
|
Java 索引
Java ArrayList中的常见删除操作及方法详解。
通过这些方法,Java `ArrayList` 提供了灵活而强大的操作来处理元素的移除,这些方法能够满足不同场景下的需求。
417 30
|
6月前
|
存储 Java
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
前言 下面,跟上主播的节奏,马上开始ThreadLocal源码的阅读( ̄▽ ̄)" 内部结构 如下图所示,我们可以知道,每个线程,都有自己的threadLocals字段,指向ThreadLocalMap
471 81
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
|
6月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
412 70
|
5月前
|
JavaScript Java 关系型数据库
家政系统源码,java版本
这是一款基于SpringBoot后端框架、MySQL数据库及Uniapp移动端开发的家政预约上门服务系统。
169 6
家政系统源码,java版本
|
5月前
|
供应链 JavaScript 前端开发
Java基于SaaS模式多租户ERP系统源码
ERP,全称 Enterprise Resource Planning 即企业资源计划。是一种集成化的管理软件系统,它通过信息技术手段,将企业的各个业务流程和资源管理进行整合,以提高企业的运营效率和管理水平,它是一种先进的企业管理理念和信息化管理系统。 适用于小微企业的 SaaS模式多租户ERP管理系统, 采用最新的技术栈开发, 让企业简单上云。专注于小微企业的应用需求,如企业基本的进销存、询价,报价, 采购、销售、MRP生产制造、品质管理、仓库库存管理、财务应收付款, OA办公单据、CRM等。
311 23

热门文章

最新文章