详解ArrayList的扩容机制

yizhihongxing

下面是讲解ArrayList的扩容机制的完整攻略:

标准版答案

概述

ArrayList 是基于数组实现的,其内部有一个数组用于存放数据。它的扩容机制就是在插入数据时,判断数组已满,此时将数组扩容为原数组长度的1.5倍。

具体实现

ArrayList 的核心代码如下:

private Object[] elementData;
private int size;

public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 判断数组是否需要扩容
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // 如果当前数组不足以容纳数据,则需要扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);  // 新数组长度为原数组的1.5倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 复制原数组
    elementData = Arrays.copyOf(elementData, newCapacity);
}

上面的代码中,add() 方法是向 ArrayList 中插入元素的方法,它先通过 ensureCapacityInternal() 方法来判断数组是否需要扩容,如果需要则调用 grow() 方法来进行扩容。

在 grow() 方法中,先根据原数组长度计算出新数组长度 newCapacity,然后将原数组中的数据复制到新数组中,最后将新数组赋给 elementData 属性。

示例说明

示例一:添加三个元素

假设 ArrayList 的初始容量为 5,首先添加三个元素:

ArrayList<Integer> list = new ArrayList<>(5); // 指定初始容量为 5
list.add(1);
list.add(2);
list.add(3);

此时,ArrayList 的 elementData 数组长度已经为 5,其中存放了三个元素。

当再次添加元素时(如添加数字 4),由于数组已满,需要进行扩容操作:

list.add(4);

此时,在执行 grow() 方法时,oldCapacity = 5,newCapacity = 7,elementData 数组被复制到了一个长度为 7 的新数组中。

示例二:手动设置容量大小并添加元素

假设想要手动指定 ArrayList 的容量大小,然后向其中添加元素。可以通过手动设置容量大小来触发扩容操作。

ArrayList<Integer> list = new ArrayList<>(5); // 指定初始容量为 5
list.ensureCapacity(10); // 手动将容量设置为 10
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6); // 此时 elementData 数组长度已满,需要进行扩容操作

在这个示例中,手动设置了 ArrayList 的容量大小为 10。向其中添加元素时,前 5 个元素不会触发扩容操作。当添加第 6 个元素时(数字 6),由于数组已满,需要进行扩容操作。

进阶版答案

概述

ArrayList 是一种基于数组实现的动态数组,如果内部数组大小不够,它会通过创建一个新数组来增加容量。该新数组是旧数组大小的1.5倍。在插入大量数据时,大量的数组复制操作可能会拖慢 ArrayList 的速度。当面对大量数据集合时,如果可以提前预测容量大小,那么在之后的数据插入中就可以减少扩容的机会,也降低了数组扩容操作的影响。

具体实现

ArrayList 内部数组的大小是其最基本的属性之一,通过调用以下构造函数可以在创建 ArrayList 时制定容量大小:

ArrayList(int initialCapacity);

在运行期间,还可以调用以下 ensureCapacity() 方法来增加 ArrayList 的容量大小:

public void ensureCapacity(int minCapacity);

如果你能够提前预测需要存储的数据量,我们可以使用该方法来避免大量数据复制操作,这样我们就可以减少在之后的插入操作中创建新数组的机会。

ArrayList<Integer> list = new ArrayList<>(); // 默认容量大小为 10
list.ensureCapacity(20); // 预先调整数组大小为 20
for (int i = 0; i < 20; ++i) {
    list.add(i);
}

在上面的代码中,我们调用 ensureCapacity() 方法预先调整 ArrayList 的大小为 20,然后我们向其中插入了 20 个整数。因为我们预先调整了大小,所以不会触发数组扩容操作。

示例说明

示例一:使用默认构造器创建 ArrayList

ArrayList<Integer> list = new ArrayList<>();
for (int i = 0; i < 20; ++i) {
    list.add(i);
}

在这个示例中,使用默认构造器创建 ArrayList,它的容量大小默认为 10。向其中插入 20 个整数后,当添加第 11 个元素时(数字 10),由于数组已满,需要进行扩容操作。接下来数组大小被扩大到 15,元素添加到了数组中。

示例二:利用 ensureCapacity() 方法提前设置容量大小

ArrayList<Integer> list = new ArrayList<>();
list.ensureCapacity(20);
for (int i = 0; i < 20; ++i) {
    list.add(i);
}

在这个示例中,我们调用 ensureCapacity() 方法提前设置容量大小为 20,然后再向其中插入 20 个整数。先预设容量大小,使得数组不会在插满 10 个元素时就进行扩容,再插入元素时避免了扩容操作,提升了程序的效率。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:详解ArrayList的扩容机制 - Python技术站

(0)
上一篇 2023年5月26日
下一篇 2023年5月26日

相关文章

  • Tomcat服务器安装配置教程(win7)

    Tomcat服务器安装配置教程(win7) 1. 下载Tomcat 首先,你需要从官网下载Tomcat服务器的安装包,你可以选择最新版本的Tomcat来下载。下载地址如下: https://tomcat.apache.org/download-80.cgi 下载后,你需要解压缩文件并将其放置在一个你所选定的目录下。 2. 配置Tomcat服务器 接下来,你需…

    Java 2023年5月19日
    00
  • IntelliJ IDEA 2020 安装和常用配置(推荐)

    IntelliJ IDEA 2020 安装和常用配置 安装 IntelliJ IDEA 2020 下载 IntelliJ IDEA 2020 的安装程序,可以到官方网站 https://www.jetbrains.com/idea/ 下载。 安装安装程序,一路默认即可,安装完成后启动软件。 常用配置 1. 设置编码格式 在项目中设置编码格式非常重要,可以避免…

    Java 2023年5月19日
    00
  • Javascript与flash交互通信基础教程

    “Javascript与Flash交互通信基础教程”指的是在一个HTML页面中,使用Javascript与Flash技术实现相互通信,从而达到一些动态效果或交互功能的目的。具体的实现方式可以通过swfobject.js插件实现,以下是详细的攻略: 步骤一:创建Flash文件 首先需要使用Flash软件创建Flash文件,并且为Flash文件命名。在编写Fla…

    Java 2023年6月15日
    00
  • java常用Lambda表达式使用场景源码示例

    Java常用Lambda表达式使用场景源码示例 什么是Lambda表达式? Lambda表达式是Java 8引入的新特性之一,它是一个匿名函数,可以传递到函数式接口中使用。Lambda表达式提供了一个简单而强大的语法来处理集合数据,比传统的循环语句更加简洁易懂。 Lambda表达式的语法格式为:(parameters) -> expression 或 …

    Java 2023年5月26日
    00
  • Java编程接口详细

    Java编程接口详细攻略 什么是Java编程接口(API) Java编程接口(API)是Java中非常重要的概念。它是一组Java类、接口和方法的集合,使得Java程序员可以轻松地使用某些功能或模块。API文档包含了Java为程序员提供的应用编程接口的详细介绍、类的功能描述和方法使用说明等。 Java API文档 Java API文档通常由类和方法的文档组成…

    Java 2023年5月19日
    00
  • 把普通对象转换成json格式的对象的简单实例

    下面是将普通对象转换成JSON格式对象的简单攻略: 准备工作 要将一个普通的对象转换成JSON格式对象,我们需要先引入JSON库(如在浏览器中使用,可以使用内置的JSON对象),然后再使用其中的方法将对象转换成JSON格式对象。 示例1 首先,我们定义一个普通对象: const obj = { name: "张三", age: 18, g…

    Java 2023年5月26日
    00
  • c#和java base64不一致的解决方法

    下面是关于“c#和java base64不一致的解决方法”的完整攻略,介绍如何解决c#和Java在base64编码上的差异问题。 问题背景 在编写应用程序时,我们经常需要将一些数据进行加密或者传输,在这个过程中,经常会用到base64编码。然而,尽管c#和Java都有对应的base64编解码方法,但是两种语言在实现上略有区别,这就导致了c#和Java在使用相…

    Java 2023年5月19日
    00
  • springboot实现返回视图而不是string的方法

    SpringBoot实现返回视图而不是String的方法 在SpringBoot中,我们可以使用Thymeleaf、Freemarker等模板引擎来实现返回视图而不是String。下面是实现返回视图的几种方法。 1. 使用Thymeleaf Thymeleaf是一种现代化的服务器端Java模板引擎,可以用于Web和独立环境。下面是一个简单的示例: 在pom.…

    Java 2023年5月18日
    00
合作推广
合作推广
分享本页
返回顶部