JDK1.8中ArrayList是如何扩容的

 更新时间:2021年12月12日 11:45:12   作者:Ccy丶双  
本文基于此出发讲解ArrayList的扩容机制,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

ArrayList简介:

ArrayList实现了List接口它是一个可调整大小的数组可以用来存放各种形式的数据。并提供了包括CRUD在内的多种方法可以对数据进行操作但是它不是线程安全的,外ArrayList按照插入的顺序来存放数据。

在讲扩容机制之前,我们需要了解一下ArrayList中最主要的几个变量:

private static final int DEFAULT_CAPACITY = 10;//数组默认初始容量

private static final Object[] EMPTY_ELEMENTDATA = {};//定义一个空的数组实例以供其他需要用到空数组的地方调用 

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//定义一个空数组,跟前面的区别就是这个空数组是用来判断ArrayList第一添加数据的时候要扩容多少。默认的构造器情况下返回这个空数组 

transient Object[] elementData;//数据存的地方它的容量就是这个数组的长度,同时只要是使用默认构造器(DEFAULTCAPACITY_EMPTY_ELEMENTDATA )第一次添加数据的时候容量扩容为DEFAULT_CAPACITY = 10 

private int size;//当前数组的长度

本题的所有的讲解都是基于JDK8

在这里插入图片描述

这道题考察了ArrayList的构造器和对扩容机制的了解,本篇博客基于此出发讲解ArrayList的扩容机制

想要做出这道题必须了解ArrayList的构造函数,ArrayList的构造函数总共有三个:

  • ArrayList()构造一个空的数组。JDK7中构造一个初始容量为10的空列表但是JDK8中只是构造一个空的数组
  • ArrayList(Collection<? extends E> c)构造一个包含指定 collection 的元素的数组,这些元素是按照该 collection 的迭代器返回它们的顺序排列的。
  • ArrayList(int initialCapacity)构造一个具有指定初始容量的空数组。

我们重点来看这两个ArrayList(int initialCapacity)ArrayList()构造函数

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

初始化一个空数组,这是JDK8不同于之前版本的地方

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);
    }
}

对于形参initialCapacity判断,如果大于0那么就声明一个和形参一样大小的数组。了解到这里似乎这道题的正确答案也出来了即选择A,并没有发生扩容

但是作为一名合格的程序员要有探索精神,题目提到了扩容,既然ArrayList底层是一个数组,那么就肯定会满,什么时候发生扩容呢?

//1.add方法为添加元素在数组末尾
public boolean add(E e) {
    //确保数组容量 size指向数组的末尾
    ensureCapacityInternal(size + 1);
    //在完成添加之前要确保数组长度足够
    elementData[size++] = e;
    return true;
}
//3.elementData为ArrayList底层维护的数组,minCapacity为此时数组的大小
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //如果数组为初始化的值,就初始化数组容量为10(空参的构造方法下首次添加)
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
//2.minCapacity表示此时数组的大小
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//4.minCapacity表示此时数组的大小
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    //如果此时数组容量的大小不够就扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

源码读到这里,我们明白了,当我们每次向ArrayList添加元素的时候,都会首先确保数组容量够放下元素如果不够就会 grow(minCapacity)调用扩容函数,那么秉承着探索的精神,原本大小的数组扩容之后变成多大了呢?还得继续看源码

//扩容源码
private void grow(int minCapacity) {
    //获取当前数组的长度
    int oldCapacity = elementData.length;
    //>>右移相当于整除2,新容量相当于就旧容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //如果扩容后的容量还不够那么就以需要的容量为新容量
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //如果新容量已经超过最大容量了,那么就直接使用最大容量
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    //讲新容量的数组拷贝
    elementData = Arrays.copyOf(elementData, newCapacity);
}

源码大致读完后,我们明白了ArrayList的自动扩容机制,每次新添加元素的时候都会判断是否能够容下,如果不够就会发生扩容,扩容的大小为原大小的1.5倍数,明白这些以后让我们看看下面这段程序扩容了几次呢??容量是多少呢?

ArrayList<Integer> arrayList = new ArrayList<Integer>(20);
for(int i=1;i<=50;i++) {
     arrayList.add(i);
}

前20次添加不会发生扩容,当21元素添加时数组容量从20扩容到30,当添加31元素时数组容量从30扩容到45,当添加46元素时数组容量从45扩容到67

到此这篇关于JDK1.8中ArrayList是如何扩容的的文章就介绍到这了,更多相关JDK1.8 ArrayList扩容内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用HTTPclient保持长连接

    使用HTTPclient保持长连接

    这篇文章主要介绍了使用HTTPclient保持长连接,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • Java+opencv3.2.0实现重映射

    Java+opencv3.2.0实现重映射

    这篇文章主要为大家详细介绍了Java+opencv3.2.0实现重映射的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-02-02
  • Mybatis批量更新对象数据的两种实现方式

    Mybatis批量更新对象数据的两种实现方式

    这篇文章主要介绍了Mybatis批量更新对象数据的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • Java算法之数组冒泡排序代码实例讲解

    Java算法之数组冒泡排序代码实例讲解

    这篇文章主要介绍了Java算法之数组冒泡排序代码实例讲解,文中用代码举例讲解的很清晰,有感兴趣的同学可以研究下
    2021-03-03
  • SpringBoot项目设置断点debug调试无效忽略web.xml问题的解决

    SpringBoot项目设置断点debug调试无效忽略web.xml问题的解决

    这篇文章主要介绍了SpringBoot项目设置断点debug调试无效忽略web.xml问题的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • SpringCloud使用CircuitBreaker实现熔断器的详细步骤

    SpringCloud使用CircuitBreaker实现熔断器的详细步骤

    在微服务架构中,服务之间的依赖调用非常频繁,当一个下游服务因高负载或故障导致响应变慢或不可用时,可能会引发上游服务的级联故障,最终导致整个系统崩溃,熔断器是解决这类问题的关键模式之一,Spring Cloud提供了对熔断器的支持,本文将详细介绍如何集成和使用它
    2025-02-02
  • SpringBoot 快速实现 api 接口加解密功能

    SpringBoot 快速实现 api 接口加解密功能

    在项目中,为了保证数据的安全,我们常常会对传递的数据进行加密,Spring Boot接口加密,可以对返回值、参数值通过注解的方式自动加解密,这篇文章主要介绍了SpringBoot 快速实现 api 接口加解密功能,感兴趣的朋友一起看看吧
    2023-10-10
  • Java 线程死锁的问题解决办法

    Java 线程死锁的问题解决办法

    这篇文章主要介绍了 Java 线程死锁的问题解决办法的相关资料,希望通过本大家能帮助到大家,遇到类似问题能够解决,需要的朋友可以参考下
    2017-09-09
  • SpringBoot项目中使用@Scheduled读取动态参数

    SpringBoot项目中使用@Scheduled读取动态参数

    这篇文章主要介绍了SpringBoot项目中使用@Scheduled读取动态参数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • SpringBoot中给指定接口加上权限校验的实现

    SpringBoot中给指定接口加上权限校验的实现

    本文介绍了使用SpringSecurity为接口添加权限校验,以防止外部访问并确保安全性,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-12-12

最新评论