java-arraylist 源码分析 1

## 深入分析 Java 中的 `ArrayList` 源码

`ArrayList` 是 Java 集合框架中的一个重要类,它基于数组实现,提供了动态数组的功能。`ArrayList` 是一个非常常用的集合类,因为它在随机访问和遍历方面性能优越。本文将详细分析 `ArrayList` 的源码,包括其数据结构、构造方法、核心操作、自动扩容机制等。

### 1. `ArrayList` 的基本数据结构

`ArrayList` 是一个动态数组,它的底层是一个 `Object` 类型的数组。在 `ArrayList` 的实现中,主要使用以下几个关键字段:

```java
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    private static final long serialVersionUID = 8683452581122892189L;

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

    // 空数组实例,当初始容量为0时使用
    private static final Object[] EMPTY_ELEMENTDATA = {};

    // 默认空数组实例,当使用默认构造函数时使用
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    // 存储元素的数组
    transient Object[] elementData;

    // 元素数量
    private int size;
}
```

- `elementData`:这是实际存储元素的数组。
- `size`:这是当前 `ArrayList` 中包含的元素数量。
- `DEFAULT_CAPACITY`:这是默认的初始容量。
- `EMPTY_ELEMENTDATA` 和 `DEFAULTCAPACITY_EMPTY_ELEMENTDATA`:分别是用于初始化时的空数组。

### 2. 构造方法

`ArrayList` 提供了多个构造方法:

#### 2.1 默认构造方法

```java
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
```

默认构造方法将 `elementData` 初始化为 `DEFAULTCAPACITY_EMPTY_ELEMENTDATA`,即一个空数组。当第一次添加元素时,数组将扩展到默认初始容量。

#### 2.2 指定初始容量的构造方法

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

此构造方法允许用户指定 `ArrayList` 的初始容量。如果初始容量大于 0,则创建一个相应大小的数组;如果初始容量为 0,则使用空数组;如果初始容量为负数,则抛出 `IllegalArgumentException`。

#### 2.3 从另一个集合创建 `ArrayList`

```java
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        this.elementData = EMPTY_ELEMENTDATA;
    }
}
```

此构造方法从另一个集合创建 `ArrayList`,并将集合中的所有元素添加到 `ArrayList` 中。

### 3. 核心操作方法

#### 3.1 添加元素

添加元素的方法主要有 `add(E e)` 和 `add(int index, E element)`:

```java
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 扩容检查
    elementData[size++] = e;
    return true;
}

public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // 扩容检查
    System.arraycopy(elementData, index, elementData, index + 1, size - index);
    elementData[index] = element;
    size++;
}
```

- `add(E e)`:在数组末尾添加元素,需要先检查容量是否足够,如果不足,则进行扩容。
- `add(int index, E element)`:在指定位置插入元素,需要先检查索引的有效性,然后将指定位置后的元素向后移动,再插入新元素。

#### 3.2 确保容量

`ensureCapacityInternal(int minCapacity)` 方法用于确保数组有足够的容量,如果不足则进行扩容:

```java
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

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

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

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
```

### 4. 自动扩容机制

当 `ArrayList` 中的元素超过当前数组的容量时,需要进行扩容。`grow(int minCapacity)` 方法用于扩容:

```java
private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    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);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}
```

- `oldCapacity`:旧容量,即扩容前数组的长度。
- `newCapacity`:新容量,为旧容量的 1.5 倍。
- 如果 `newCapacity` 仍小于所需的最小容量,则将其设为 `minCapacity`。
- 如果 `newCapacity` 超过了 `MAX_ARRAY_SIZE`,则调用 `hugeCapacity(int minCapacity)` 方法进行处理。
- 最后通过 `Arrays.copyOf` 方法将旧数组复制到新数组中。

### 5. 删除元素

`ArrayList` 提供了删除元素的方法 `remove(int index)` 和 `remove(Object o)`:

```java
public E remove(int index) {
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

private void fastRemove(int index) {
    modCount++;
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index, numMoved);
    elementData[--size] = null; // clear to let GC do its work
}
```

- `remove(int index)`:根据索引删除元素,首先检查索引的有效性,然后通过 `System.arraycopy` 方法将索引后的元素向前移动,覆盖要删除的元素。
- `remove(Object o)`:根据对象删除元素,通过遍历找到目标元素并调用 `fastRemove(int index)` 方法进行删除。

### 6. 获取元素和修改元素

`ArrayList` 提供了获取元素的方法 `get(int index)` 和修改元素的方法 `set(int index, E element)`:

```java
public E get(int index) {
    rangeCheck(index);
    return elementData(index);
}

public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}
```

- `get(int index)`:根据索引获取元素,首先检查索引的有效性,然后返回数组中的对应元素。
- `set(int index, E element)`:根据索引修改元素,首先检查索引的有效性,然后将指定位置的元素替换为新元素,并返回旧元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/40411.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

spring cloud gateway客户端websocket断开连接,服务侧连接没有关闭的问题处理

之前在单体架构项目中使用了websocket主动推送消息的功能&#xff0c;后来改成了微服务架构&#xff0c;结果发现部分消息丢失&#xff0c;没能推送给客户端&#xff1b;深入排查发现服务端无法感知websocket连接状态&#xff0c;但是在单体架构里面是没这个问题的&#xff0c;…

Redis【超详细】

Redis 是一个基于内存的key-value结构的数据库 一、redis的安装 1.1、安装步骤 1&#xff09;安装Redis依赖 Redis是基于c语言编写的&#xff0c;因此需要安装对应的gcc环境 yum install -y gcc tcl 2&#xff09;进入/usr/local/src/目录上传并解压安装包 解压&#xf…

【APK】SDKManager运行后闪退

本地JDK已安装&#xff0c;且配置了环境变量&#xff0c;未安装 android studiio 问题描述&#xff1a;右键以管理员身份运行 SDKManager&#xff0c;终端窗口闪退 问题原因&#xff1a;未找到正确的Java路径 解决办法&#xff1a; 1.修改tools目录下的 android.bat 文件&am…

langchain 入门中篇:数据封装,Memory 封装

数据的处理流程可以看一张图来帮助理解 数据来源可以是网络&#xff0c;可以是邮件&#xff0c;可以是本地文件 经过 Document Loaders 加载&#xff0c;再在 Transform 阶段对文档进行 split, filter, translate, extract metadata 等操作&#xff0c;之后在 Embed 阶段进行向…

Keil用ST-LINK下载STM32程序后不自动运行

之后程序可以运行了&#xff0c;但是串口还没有输出&#xff0c;在debug模式下都是ok的。

加权 KNN 算法的原理与详解

加权kNN&#xff0c;k近邻算法的增强改进版本。 加权KNN算法 近邻算法&#xff08;k-Nearest Neighbors, kNN&#xff09;是一种用于分类和回归的非参数方法。它的基本思想是“看邻居”&#xff0c;即通过查找离目标点最近的 K 个数据点&#xff0c;来判断目标点的类别或数值。…

docker安装elasticesarch-head

安装 Elasticsearch-Head 通常涉及以下步骤&#xff1a; 拉取 Elasticsearch-Head 的 Docker 镜像。 运行 Elasticsearch-Head 容器并连接到 Elasticsearch 实例。 以下是具体的命令&#xff1a; 拉取 Elasticsearch-Head 的 Docker 镜像 docker pull mobz/elasticsearch-…

Sqlserver 如何创建全局只读账号?

由于SQL Server不支持全局数据库权限&#xff0c;因此需要在每个数据库中创建用户并授予其只读权限。可以使用动态SQL脚本来为所有现有数据库设置权限&#xff0c;具体脚本如下 ##创建登陆账号CREATE LOGIN user01 WITH PASSWORD password; ##除了系统库外给user01 db_datare…

FactoryBean原理及用法

它的作用是用制造创建过程较为复杂的产品, 如 SqlSessionFactory, 但 Bean 已具备等价功能 使用 被 FactoryBean 创建的产品 会认为创建、依赖注入、Aware 接口回调、前初始化这些都是 FactoryBean 的职责, 这些流程都不会走 唯有后初始化的流程会走, 也就是产品可以被代理增…

学习aurora64/66b.20240703

简介 The AMD LogiCORE™IP Aurora 64B/66B core是一种可扩展的轻量级高数据速率链路层协议&#xff0c;用于高速串行通信。该协议是开放的&#xff0c;可以使用AMD设备技术实现。 Aurora 64B/66B是一种轻量级的串行通信协议&#xff0c;适用于多千兆位链路 (如下图所示)。它…

【MATLAB源码-第139期】基于matlab的OFDM信号识别与相关参数的估计,高阶累量/小波算法调制识别,循环谱估计,带宽估计,载波数目估计等等。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 在现代无线通信系统中&#xff0c;正交频分复用&#xff08;OFDM&#xff09;因其高效的频谱利用率、强大的抗多径衰落能力以及灵活的带宽分配等优势&#xff0c;成为了一种非常重要的调制技术。然而&#xff0c;随着无线通信…

采沙船智能监测识别摄像机

对于现代河流管理来说&#xff0c;采沙船智能监测识别摄像机正逐渐成为解决非法采砂和保护河流生态环境的重要工具。这类摄像机通过先进的视觉识别和数据分析技术&#xff0c;有效监控和管理河道上的采沙行为&#xff0c;对保护水域资源和改善生态环境具有显著的意义。 采沙船智…

Linux容器篇-使用kubeadm搭建一个kubernetes集群

kubernetes集群架构和组件 master节点组件 kube-apiserver&#xff1a;Kubernetes API&#xff0c;集群的统一入口&#xff0c;各组件的协调者&#xff0c;以RESTful API提供接口服务&#xff0c;所有对象资源的增删改查和监听操作都交给APIserver处理后再交给Etcd存储。 kube…

学习Mybatis

Mybatis 第一节 引言 1. 什么是框架 框架是一个半成品&#xff0c;解决了软件开发过程中的普遍性问题&#xff0c;简化了开发步骤&#xff0c;提高了开发效率。 2. 什么是ORM ORM全称为Object Relational Mapping&#xff0c;意为对象关系映射&#xff0c;主要实现了将程序…

usecallback()与usememo()

简单的说 都是用来监听数据变化 来进行控制渲染、减少不必要的渲染 、优化性能 usecallback()是用来监听数据变化从而调用方法 usememo()是用来监听数据变化从而改变数据 使用return返回变化的数据 当然return 也可以返回方法 所以usememo()可以代替usecallback() 下面详解 …

常见的编码技术简介

常见的编码技术简介 文章目录 常见的编码技术简介1. 字符编码1.1 ASCII1.2 Unicode 2. 数据传输编码2.1 Base系列编码2.1.1 Base642.1.2 Base162.1.3 Base322.1.4 Base852.1.5 其他Base编码 2.2 URL编码2.3 JSON2.4 XML2.5 Protobuf (Protocol Buffers) 1. 字符编码 1.1 ASCII…

AI是在帮助开发者还是取代他们?——探讨AI在软件开发中的角色与未来

引言 随着人工智能技术的迅猛发展&#xff0c;AI工具在软件开发中的应用越来越广泛。有人认为AI可以显著提升开发者的效率&#xff0c;而也有人担心AI会取代开发者的工作。本文将从三个方面探讨AI在软件开发中的角色&#xff1a;AI工具现状、AI对开发者的影响以及AI开发的未来…

学习springAOP

第三章 Spring AOP 第一节 AOP 简介 1. 概念 AOP全称为Aspect Oriented Programming&#xff0c;表示面向切面编程。何为切面呢&#xff1f; 由此可以得出&#xff0c;切面是一种将那些与业务无关&#xff0c;但业务模块都需要使用的功能封装起来的技术。这样便于减少系统的…

昇思25天学习打卡营第4天|应用实践

昇思25天学习打卡营第4天 文章目录 昇思25天学习打卡营第4天基于 MindSpore 实现 BERT 对话情绪识别模型简介环境配置数据集数据加载和数据预处理input_idsattention_mask 模型构建模型验证模型推理自定义推理数据集 打卡记录 基于 MindSpore 实现 BERT 对话情绪识别 模型简介…

奥比中光astra_pro相机使用记录

一、信息获取 1、官网 用于了解产品信息 http://www.orbbec.com.cn/sys/37.html 2、开发者社区 咨询问题下载开发部https://developer.orbbec.com.cn/ 二 、windowvs19 1、相机型号 orbbec_astro_pro 根据对应的型号找到需要的包工具 踩坑1&#xff0c;因为这个相机型号…