Java的ArrayList扩容源码解析

 更新时间:2024年01月11日 10:02:56   作者:好奇的7号  
这篇文章主要介绍了Java的ArrayList扩容源码解析,通过动态扩容,ArrayList能够在添加元素时保持高效的性能,扩容操作是有一定开销的,但由于扩容的时间复杂度为O(n),其中n是当前元素个数,所以平均情况下,每次添加元素的时间复杂度仍然是O(1),需要的朋友可以参考下

ArrayList扩容源码

1、扩容原理概括

ArrayList的扩容原理如下:

  • 初始容量:在创建ArrayList对象时,默认会分配一个初始容量。初始容量可以通过构造函数进行指定,若未指定,则默认为10。例如,ArrayList<String> list = new ArrayList<>();会创建一个初始容量为10的ArrayList。
  • 添加元素:当向ArrayList中添加元素时,会先检查当前元素个数是否已经达到了数组的容量上限。如果元素个数等于或超过了容量上限,就需要进行扩容。
  • 扩容机制:扩容时,ArrayList会创建一个更大的新数组,并将原有的元素复制到新数组中。默认情况下,新数组的大小是原来容量的1.5倍(即增长50%)。例如,如果当前容量为10,那么扩容后的新容量为15。
  • 数组复制:在扩容时,ArrayList使用System.arraycopy()方法将原始数组中的元素复制到新的数组中。这是一个底层的高效数组复制方法,可以快速地将原有的数据移动到新数组中。
  • 更新引用:在完成数组复制后,ArrayList会更新内部的引用,指向新的数组。这样,原先的数组就会被垃圾回收器回收。

通过动态扩容,ArrayList能够在添加元素时保持高效的性能。扩容操作是有一定开销的,但由于扩容的时间复杂度为O(n),其中n是当前元素个数,所以平均情况下,每次添加元素的时间复杂度仍然是O(1)。

同时,扩容的增长因子(即容量的增加比例)也可以被修改,以满足特定需求。

2、源码解析

例如,对于如下list进行添加元素,初始容量设置为5,添加6个元素:

ArrayList<String> list = new ArrayList<>(5);
list.add("1");
list.add("2");
list.add("3");
list.add("777");
list.add("888");
list.add("999");

对于一开始的前五个元素而言:

public boolean add(E e) {
        modCount++;
        add(e, elementData, size);//e是“1”,size是0,即目前元素数量,也可以理解为数组的索引
        return true;
}
 
//add源码:
private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)//当元素数量等于arraylist数组长度,才grow扩容
            elementData = grow();
        elementData[s] = e;//否则直接在索引位置赋值
        size = s + 1;//并将索引右移
}

但在第6个元素“999”添加进去的时候,要进入grow方法进行扩容:

private void add(E e, Object[] elementData, int s) { //e是“999”,s为5
        if (s == elementData.length)//已经相等
            elementData = grow();//步入grow
        elementData[s] = e;
        size = s + 1;
}
 
//grow源码:输入size + 1
private Object[] grow(int minCapacity) {
        int oldCapacity = elementData.length;//旧的数组大小
 
//接下来,检查当前数组是否为空,或者不是使用默认初始容量的空数组(即DEFAULTCAPACITY_EMPTY_ELEMENTDATA)。如果是空数组,则说明是第一次添加元素,使用默认初始容量。
        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            int newCapacity = ArraysSupport.newLength(oldCapacity,
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);//计算新数组大小
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } else {
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
}
 
//ArraysSupport.newLength源码:
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
        // assert oldLength >= 0
        // assert minGrowth > 0
//minGrowth就是6 - 5 = 1,即,至少需要增大的容量
//prefGrowth为数组的旧容量的1/2
//求其max,一般为1/2。加上oldlength,所以为1.5倍
        int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
        if (newLength - MAX_ARRAY_LENGTH <= 0) {
            return newLength;
        }
        return hugeLength(oldLength, minGrowth);
    }
 
//最后调用Arrays.copyOf,内部使用System.arraycopy()方法将原始数组中的元素复制到新的数组中

实现扩容后,添加元素步骤同上,结束,return true

到此这篇关于Java的ArrayList扩容源码解析的文章就介绍到这了,更多相关ArrayList扩容源码内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 解决druid监控页面SQL不显示的问题

    解决druid监控页面SQL不显示的问题

    这篇文章主要介绍了解决druid监控页面SQL不显示的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Sentinel自定义异常的三种实现方式

    Sentinel自定义异常的三种实现方式

    Spring Cloud Alibaba Sentinel 是目前主流并开源的流量控制和系统保护组件,Spring Cloud Alibaba Sentinel 有 3 种自定义异常的实现方式,本文小编将通过代码示例给大家详细的介绍这三种实现方式,需要的朋友可以参考下
    2023-11-11
  • 一小时迅速入门Mybatis之增删查改篇

    一小时迅速入门Mybatis之增删查改篇

    这篇文章主要介绍了迅速入门Mybatis之增删查改篇,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-09-09
  • 使用apache 的FileUtils处理文件的复制等操作方式

    使用apache 的FileUtils处理文件的复制等操作方式

    这篇文章主要介绍了使用apache 的FileUtils处理文件的复制等操作方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-07-07
  • IDEA 的基本介绍使用及断点调试技巧

    IDEA 的基本介绍使用及断点调试技巧

    IDEA 是 JetBrains 公司的产品,总部位于捷克的首都布拉格,IDEA在业界被公认为最好的 Java 开发工具,今天通过本文给大家介绍IDEA 的基本介绍使用及断点调试技巧,感兴趣的朋友跟随小编一起看看吧
    2021-11-11
  • 教你用Java实现一个简单的代码生成器

    教你用Java实现一个简单的代码生成器

    今天给大家带来的是关于Java的相关知识,文章围绕着如何用Java实现一个简单的代码生成器展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • redis分布式锁解决表单重复提交的问题

    redis分布式锁解决表单重复提交的问题

    在系统中,有些接口如果重复提交,可能会造成脏数据或者其他的严重的问题,所以我们一般会对与数据库有交互的接口进行重复处理。本文就详细的介绍一下redis分布式锁解决表单重复提交,感兴趣的可以了解一下
    2021-11-11
  • Spring Boot集成RabbitMQ以及队列模式操作

    Spring Boot集成RabbitMQ以及队列模式操作

    RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,下面这篇文章主要给大家介绍了关于Spring Boot集成RabbitMQ以及队列模式操作的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-04-04
  • java实现CSV文件导入与导出功能

    java实现CSV文件导入与导出功能

    这篇文章主要为大家详细介绍了java实现CSV文件导入与导出,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-08-08
  • 使用@ControllerAdvice同时配置过滤多个包

    使用@ControllerAdvice同时配置过滤多个包

    这篇文章主要介绍了使用@ControllerAdvice同时配置过滤多个包的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06

最新评论