详解ArrayList的扩容机制

下面是讲解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日

相关文章

  • Eclipse配置maven环境的图文教程

    下面我就为你详细讲解“Eclipse配置maven环境的图文教程”的完整攻略。 准备工作 在开始配置maven环境前,我们需要先下载和安装maven,具体步骤如下: 访问Maven官网(https://maven.apache.org/),并下载对应操作系统的安装包; 解压下载的压缩包到指定的目录下,比如D:\Program Files\apache-mav…

    Java 2023年5月20日
    00
  • SpringBoot浅析安全管理之Spring Security配置

    让我来详细讲解一下“SpringBoot浅析安全管理之Spring Security配置”的完整攻略。 概述 Spring Security是一个功能强大且灵活的框架,它为我们提供了许多功能,包括身份验证,授权,安全性配置等。本篇文章将介绍如何在Spring Boot项目中配置Spring Security。 依赖项 首先,请确保您已经添加了Spring S…

    Java 2023年5月20日
    00
  • Springboot使用Spring Data JPA实现数据库操作

    下面我将为您详细讲解“Springboot使用Spring Data JPA实现数据库操作”的完整攻略。 简介 Spring Data JPA是Spring框架下的一个项目,其主要目的是简化数据访问层的开发,并提供了更加优雅(简单)的方式来对关系型数据库进行操作。 步骤一:添加依赖 要想使用Spring Data JPA,我们需要先在pom.xml文件中添加…

    Java 2023年5月20日
    00
  • 使用Sharding-JDBC对数据进行分片处理详解

    那么让我们来详细讲解如何使用Sharding-JDBC对数据进行分片处理。 什么是Sharding-JDBC Sharding-JDBC是一种基于JDBC的轻量级Java框架,用于将数据库水平分片。Sharding-JDBC通过拦截JDBC API调用来实现透明的数据分片,所以你可以使用任何基于JDBC的ORM框架(如Hibernate、MyBatis、JP…

    Java 2023年6月16日
    00
  • SpringBoot多种自定义错误页面方式小结

    首先我们来介绍一下SpringBoot的错误页面。SpringBoot的错误页面一般可以分为以下两种: 默认错误页面 SpringBoot自带了默认的错误页面,在出现错误时会自动跳转到该页面。默认的错误页面包含了错误的状态码、错误信息和错误堆栈等信息。如果你没有设置自定义的错误页面,那么就会默认跳转到该页面。 自定义错误页面 SpringBoot还支持开发者…

    Java 2023年5月25日
    00
  • ajax对注册名进行验证检测是否存在于数据库中

    检测注册名是否已存在于数据库中是Web开发中常见的需求之一,而Ajax技术则常被用来实现前端异步验证。下面,我将为您讲解实现这一需求的完整攻略。 1. 前端实现 前端实现的主要流程如下: 给用户名输入框绑定事件 监听输入框的值变化,触发Ajax请求 将输入框的值作为参数发送给后端API 根据API的返回结果,展示相应的提示信息 示例代码如下: <inp…

    Java 2023年6月15日
    00
  • SpringBoot环境下junit单元测试速度优化方式

    下面是详细讲解“SpringBoot环境下junit单元测试速度优化方式”的完整攻略。 SpringBoot环境下junit单元测试速度优化方式 背景 在我们进行Java项目的开发过程中,经常需要编写单元测试用例来验证程序的正确性。在进行单元测试时,测试用例的执行速度非常重要。 现在大多数Java项目都采用了SpringBoot框架来进行开发和测试。在这种情…

    Java 2023年5月20日
    00
  • JavaSwing基础之Layout布局相关知识详解

    JavaSwing是用于开发桌面应用程序的一套GUI工具包,其中Layout布局是Swing中常用的一种布局方式。此篇文章将详细讲解Layout布局的相关知识,为JavaSwing的使用提供帮助。 布局方式 Swing提供了多种布局方式,其中常见的有FlowLayout、BorderLayout、GridLayout、GridBagLayout、BoxLay…

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