深入源码解析ArrayList:探秘Java动态数组的机制与性能

文章目录

    • 一、 简介ArrayList
      • 1.1 介绍ArrayList的基本概念和作用
      • 1.2 与数组的区别和优势
    • 二、 内部实现
      • 2.1 数据结构:动态数组
      • 2.2 添加元素:add()方法的实现原理
      • 2.3 扩容机制:ensureCapacity()方法的实现原理
    • 三、 常见操作分析
      • 3.1 获取元素:get()方法的实现原理
      • 3.2 删除元素:remove()方法的实现原理
      • 3.3 修改元素:set()方法的实现原理
    • 四、 性能分析
      • 4.1 时间复杂度分析
      • 4.2 空间复杂度分析
      • 4.3 与LinkedList的比较
    • 五、 源码解读
      • 5.1 成员变量
      • 5.2 构造方法
      • 5.3 trimToSize()方法
      • 5.4 indexOf()方法
      • 5.5 clone()方法
      • 5.6 get()方法
      • 5.7 set()方法
      • 5.8 add()方法
      • 5.9 remove()方法
      • 5.10 addAll()方法
    • 六、 案例分析与实例演示
      • 6.1 案例分析
      • 6.2 实例演示

一、 简介ArrayList

1.1 介绍ArrayList的基本概念和作用

在Java中,ArrayList是一个实现了List接口的动态数组。它可以根据需要自动增加大小,因此可以存储任意数量的元素。

  1. 基本概念:
    • ArrayList是Java中常用的集合类之一,它可以存储对象,并且可以根据索引访问和操作这些对象。
    • ArrayList是基于数组实现的,但是它具有动态扩展的能力,因此可以动态地增加和减少元素的数量。
  2. 作用:
    • 存储数据:ArrayList可以用来存储各种类型的数据,包括基本类型和对象类型。
    • 动态扩展:由于ArrayList的大小是动态的,因此它非常适合需要动态增加和减少元素的场景。
    • 方便操作:ArrayList提供了丰富的方法来操作元素,比如添加、删除、查找等,使得对集合的操作变得非常便利。

总之,ArrayList在Java中是非常常用的数据结构,它提供了动态存储数据的能力,以及丰富的操作方法,非常适合在开发中使用。

1.2 与数组的区别和优势

ArrayList是一种动态数组,它是基于数组实现的,但具有动态扩展和收缩的能力。与普通数组相比,ArrayList具有以下区别和优势:

  1. 大小动态性:ArrayList的大小是动态的,可以根据需要动态扩展和收缩。而普通数组的大小是固定的,一旦创建就无法改变。
  2. 自动扩展:当ArrayList中的元素数量超过当前容量时,ArrayList会自动进行扩展,而普通数组需要手动重新分配内存并复制数据。
  3. 插入和删除元素效率高:ArrayList支持在任意位置插入和删除元素,而普通数组在插入和删除元素时需要移动其他元素。
  4. 内置方法和功能:ArrayList提供了许多便捷的方法和功能,如添加、删除、查找等操作,使其更易于使用和操作。

总的来说,ArrayList相对于普通数组来说更加灵活、便捷,并且具有更高的操作效率。因此,在大多数情况下,使用ArrayList比使用普通数组更加方便和实用。

二、 内部实现

2.1 数据结构:动态数组

在Java中,ArrayList是一个动态数组实现的类,它是基于数组实现的动态数组,可以自动扩容。下面是ArrayList的动态数组原理:

  1. 内部数组:ArrayList内部使用一个数组来存储元素。当创建一个ArrayList时,会初始化一个初始容量的数组。
  2. 自动扩容:当向ArrayList中添加元素时,如果当前数组已满,ArrayList会创建一个新的更大容量的数组,并将原数组中的元素复制到新数组中,然后将新元素添加到新数组中。
  3. 扩容策略:ArrayList的扩容策略是在每次扩容时将当前容量扩大为原来的1.5倍,这种策略既能够保证空间利用率,又能够减少因频繁扩容而带来的性能开销。
  4. 随机访问:由于ArrayList内部基于数组实现,因此支持随机访问,可以通过索引直接访问数组中的元素,时间复杂度为O(1)

总的来说,ArrayList通过动态扩容的方式,利用数组实现了一个动态数组,提供了高效的随机访问和动态增删元素的功能。

2.2 添加元素:add()方法的实现原理

在Java中,ArrayList的add()方法用于向ArrayList中添加元素。

其实现原理如下:

  1. 在调用add()方法时,先检查当前ArrayList的大小和容量(即存储空间是否足够)。
  2. 如果当前容量不够,就进行扩容操作。一般情况下,会创建一个新的数组,将原数组中的元素复制到新数组中,并且为新数组分配更大的存储空间。
  3. 然后将要添加的元素放入ArrayList的内部数组中,并更新ArrayList的大小。
  4. 如果添加成功,则返回true,如果添加失败,则返回false

总的来说,ArrayList的add()方法实现原理就是对内部数组的扩容和元素的添加操作。

2.3 扩容机制:ensureCapacity()方法的实现原理

在使用ArrayList时,如果我们预先知道将要插入的元素数量,可以使用ensureCapacity()方法来预先分配内部数组大小。调用了一个名为ensureCapacityInternal()的私有方法。这个方法首先会判断当前内部数组是否已经足够大来容纳新增的元素,如果不够大,则会进行扩容:

  1. 如果当前内部数组为空,则直接扩容到指定容量大小。
  2. 否则,计算出新数组的容量大小,这个容量大小取决于原始数组的大小和扩容因子(默认为1.5倍)。
  3. 如果新数组容量大小小于所需容量,则按照所需容量分配新的数组;否则,按照新数组容量大小分配新的数组。
  4. 将原始数组中的元素复制到新数组中,并将新数组赋值给ArrayList对象的elementData变量。
import java.util.ArrayList;

public class EnsureCapacityExample {
    public static void main(String[] args) {
        // 创建一个空的ArrayList
        ArrayList<String> list = new ArrayList<>();

        // 预先设定ArrayList内部数组的容量为20
        list.ensureCapacity(20);

        // 现在,ArrayList的内部数组至少可以容纳20个元素

        // 添加元素到ArrayList
        list.add("Element 1");
        list.add("Element 2");
        list.add("Element 3");

        // ...
    }
}

通过使用ensureCapacity()方法,我们可以避免由于频繁扩容带来的性能损失,提高程序效率。

三、 常见操作分析

3.1 获取元素:get()方法的实现原理

在Java中,ArrayList的get()方法实际上是通过调用数组的索引来获取指定位置的元素。ArrayList内部维护了一个Object类型的数组来存储元素。

  • 当调用get()方法时,ArrayList会将传入的索引作为数组的下标,直接访问数组中对应位置的元素,并返回该元素。
  • 因为数组的访问是基于内存地址的,所以获取元素的时间复杂度为O(1),即常数时间复杂度。
  • ArrayList的get()方法并不会对数组进行拷贝或重新分配空间,因此在获取元素时是非常高效的。
  • 频繁进行插入、删除等涉及数组扩容的操作,可能会导致性能下降。

ArrayList的get()方法通过直接访问底层数组的方式快速获取指定位置的元素。

3.2 删除元素:remove()方法的实现原理

Java中的ArrayList类是基于数组实现的动态数组,当我们使用remove()方法从ArrayList中删除元素时,这个方法会将指定位置的元素从内部数组中移除,并将后续元素向前移动一位。

这个操作可以通过以下几个步骤来实现:

  1. 检查待删除的元素下标是否越界,如果越界则抛出IndexOutOfBoundsException异常。
  2. 将要删除的元素从内部数组中移除,这个过程可以通过System.arraycopy()方法来实现,该方法可以将数组的某一范围内的元素复制到另一个位置上。
  3. 将后续元素向前移动一位,以填补被删除的空位。这个过程同样可以通过System.arraycopy()方法来实现。

ps:ArrayList的remove()方法只能移除第一个与指定元素相等的元素。如果我们想要移除所有等于指定元素的元素,可以通过循环遍历ArrayList并使用remove()方法来实现。

3.3 修改元素:set()方法的实现原理

Java中的ArrayList是一种基于数组的动态数组实现,它继承了AbstractList类并实现了List接口。set()方法是ArrayList中的一个方法,用于将指定索引位置的元素替换为新的元素。

其实现原理如下

  1. 首先,set()方法会检查传递的索引是否在ArrayList范围之内。如果索引小于0或大于等于ArrayList的大小(size()方法返回的值),则会抛出IndexOutOfBoundsException异常。
  2. 如果索引有效,则会使用数组的索引定位到指定的元素,并将其替换为新的元素。
  3. set()方法返回被替换掉的元素。
  4. 在替换元素时,ArrayList可能需要调整内部数组的大小。如果新元素的大小与当前数组的容量不匹配,ArrayList会创建一个新数组,并将所有元素从旧数组复制到新数组中。

总之,ArrayList的set()方法的实现原理是通过数组索引定位和替换元素来完成的,而且可能需要动态调整内部数组的大小。

四、 性能分析

4.1 时间复杂度分析

在Java中,ArrayList是一个动态数组实现的集合类,它提供了随机访问和快速插入/删除元素的功能。

下面是ArrayList的常见操作及其时间复杂度分析

  1. 访问元素(get):通过索引访问特定位置的元素,时间复杂度为O(1)
  2. 插入元素(add):在指定位置插入元素,平均时间复杂度为O(n),最坏情况下需要将插入位置之后的元素都向后移动,时间复杂度为O(n)
  3. 删除元素(remove):删除指定位置的元素,平均时间复杂度为O(n),最坏情况下需要将删除位置之后的元素都向前移动,时间复杂度为O(n)
  4. 查找元素(contains):判断集合中是否包含某个元素,平均时间复杂度为O(n),需要遍历整个集合来查找。
  5. 获取集合大小(size):获取集合中元素的数量,时间复杂度为O(1)

需要注意的是,ArrayList的插入和删除操作涉及到元素的移动,当集合的大小较大时,这些操作可能会导致性能下降。如果需要频繁进行插入和删除操作,可以考虑使用LinkedList来代替ArrayList,因为LinkedList对于插入和删除操作的时间复杂度是O(1)

4.2 空间复杂度分析

ArrayList的空间复杂度主要取决于两个因素:集合中的元素数量和内部数组的容量。

  1. 元素数量:ArrayList存储的元素数量,即集合的大小,会占用一定的空间。假设元素数量为n,则空间复杂度为O(n)
  2. 内部数组容量:ArrayList内部使用一个动态数组来存储元素,数组的容量可能会比集合的大小大一些,以容纳未来添加的元素。假设数组的容量为m,则空间复杂度为O(m)

需要注意的是,ArrayList的实际空间占用可能会比集合中的元素数量多一些,因为它预留了一些额外的容量供后续添加元素使用。当集合的元素数量接近或超过内部数组的容量时,ArrayList会自动进行扩容操作,重新分配更大的数组并将原有元素复制到新数组中,这可能会导致空间复杂度的增加。

在实际使用中,可以通过调整ArrayList的初始容量或使用构造函数指定初始容量来控制空间复杂度。通常情况下,如果能够预估集合的大小,设置一个适当的初始容量可以减少扩容操作的频率,提高性能。

4.3 与LinkedList的比较

ArrayList和LinkedList是Java中两种常见的集合类,它们都实现了List接口,但在内部实现和性能特点上有所不同。

下面是ArrayList和LinkedList的比较

  1. 内部实现
    • ArrayList:使用动态数组实现,内部维护一个可变长度的数组来存储元素。
    • LinkedList:使用双向链表实现,内部由一系列节点组成,每个节点包含元素值和前后指针。
  2. 访问效率
    • ArrayList:由于使用数组实现,可以通过索引直接访问元素,因此随机访问的效率很高,时间复杂度为O(1)。但在插入和删除元素时,需要移动数组中的元素,效率较低,时间复杂度为O(n)
    • LinkedList:插入和删除元素的效率较高,因为只需要调整节点的指针,时间复杂度为O(1)。但在随机访问元素时,需要从头节点开始按序遍历查找,效率较低,时间复杂度为O(n)
  3. 空间占用
    • ArrayList:使用动态数组,会预留一定容量的空间,当元素数量超过容量时,需要进行扩容操作。因此,可能会有额外的空间浪费。
    • LinkedList:使用链表结构,每个节点除了存储元素还需要存储前后节点的指针,因此会略微增加一些额外空间。
  4. 适用场景:
    • ArrayList:适合于随机访问和遍历操作较多的场景,例如根据索引访问元素、遍历集合等。但在频繁插入和删除元素的情况下,性能相对较差。
    • LinkedList:适合于频繁插入和删除元素的场景,例如实现队列或栈等数据结构。但在随机访问元素时,性能相对较差。

五、 源码解读

5.1 成员变量

在这里插入图片描述

5.2 构造方法

在这里插入图片描述

5.3 trimToSize()方法

在这里插入图片描述

5.4 indexOf()方法

在这里插入图片描述

5.5 clone()方法

在这里插入图片描述

5.6 get()方法

在这里插入图片描述

5.7 set()方法

在这里插入图片描述

5.8 add()方法

在这里插入图片描述

5.9 remove()方法

在这里插入图片描述

5.10 addAll()方法

在这里插入图片描述

六、 案例分析与实例演示

6.1 案例分析

假设有一个学生管理系统,需要存储学生的信息,包括姓名、年龄、性别等。

为了方便管理,我们可以使用ArrayList来存储学生对象。

首先定义一个学生类,包含姓名、年龄、性别三个属性

public class Student {
    private String name;
    private int age;
    private String gender;

    public Student(String name, int age, String gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    // getter 和 setter 方法省略
}

然后在主类中创建ArrayList对象,并添加学生信息

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        // 创建ArrayList对象
        ArrayList<Student> list = new ArrayList<>();

        // 添加学生信息
        list.add(new Student("张三", 18, "男"));
        list.add(new Student("李四", 20, "女"));
        list.add(new Student("王五", 19, "男"));

        // 遍历学生信息
        for (Student student : list) {
            System.out.println("姓名:" + student.getName() + " 年龄:" + student.getAge() + " 性别:" + student.getGender());
        }
    }
}

输出结果如下

姓名:张三 年龄:18 性别:男
姓名:李四 年龄:20 性别:女
姓名:王五 年龄:19 性别:男

6.2 实例演示

演示一下如何使用ArrayList实现一个简单的购物车程序。

首先定义一个商品类,包含名称和价格两个属性

public class Product {
    private String name;
    private double price;

    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }

    // getter 和 setter 方法省略
}

然后在主类中创建ArrayList对象,并添加商品信息

import java.util.ArrayList;
import java.util.Scanner;

public class ShoppingCart {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);

        // 创建ArrayList对象
        ArrayList<Product> cart = new ArrayList<>();

        // 添加商品信息
        cart.add(new Product("可乐", 3.5));
        cart.add(new Product("薯片", 5.0));
        cart.add(new Product("巧克力", 8.0));

        // 输出商品信息
        System.out.println("欢迎来到购物车!");
        for (Product product : cart) {
            System.out.println(product.getName() + " 价格:" + product.getPrice());
        }

        // 计算总价
        double totalPrice = 0;
        while (true) {
            System.out.print("请输入要购买的商品编号(输入-1结束):");
            int index = scanner.nextInt();
            if (index == -1) {
                break;
            }
            Product product = cart.get(index);
            System.out.println("已选择 " + product.getName() + " 价格:" + product.getPrice());
            totalPrice += product.getPrice();
        }
        System.out.println("总价:" + totalPrice);
    }
}

运行程序,输出结果如下

欢迎来到购物车!
可乐 价格:3.5
薯片 价格:5.0
巧克力 价格:8.0
请输入要购买的商品编号(输入-1结束):0
已选择 可乐 价格:3.5
请输入要购买的商品编号(输入-1结束):1
已选择 薯片 价格:5.0
请输入要购买的商品编号(输入-1结束):2
已选择 巧克力 价格:8.0
请输入要购买的商品编号(输入-1结束):-1
总价:16.5

盈若安好,便是晴天

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Postswigger 靶场 XSS 通关

文章目录 PostSwigger靶场XSS通关学徒&#xff1a;第一关学徒&#xff1a;第二关学徒&#xff1a;第三关学徒&#xff1a;第四关学徒&#xff1a;第五关学徒&#xff1a;第六关学徒&#xff1a;第七关学徒&#xff1a;第八关学徒&#xff1a;第九关 PostSwigger靶场XSS通关 靶…

2023_Spark_实验二十六:编写Shell模拟生成点击实时数据

引言&#xff1a;流式数据处理主要处理实时数据&#xff0c;由于实验教学过程中&#xff0c;每个同学无法拿到实时数据&#xff0c;因此我们开发shell脚本模拟实时数据生成&#xff0c;支持后续实验。 实验目的&#xff1a;通过开发模拟实时点击流shell脚本&#xff0c;模拟实时…

<JavaEE> 经典设计模式之 -- 线程池

目录 一、线程池的概念 二、Java 标准库中的线程池类 2.1 ThreadPoolExecutor 类 2.1.1 corePoolSize 和 maximumPoolSize 2.1.2 keepAliveTime 和 unit 2.1.3 workQueue 2.1.4 threadFactory 2.1.5 handler 2.1.6 创建一个参数自定义的线程池 2.2 Executors 类 2.3…

java答题小程序源码带后台

尊敬的客户大家好&#xff01;接下来由我来介绍一下晟讯答题小程序&#xff0c;晟讯答题小程序是一款专业性的答题小程序&#xff0c;技术方式为前端原生开发的小程序&#xff0c;服务端为java程序&#xff0c;且拥有独立知识产权&#xff0c;软著登字2019SR0657453。其功能集个…

全球化表达:TikTok在文化交流中的崭露头角

TikTok&#xff0c;这一短视频平台自问世以来&#xff0c;迅速蔓延至全球&#xff0c;成为年轻一代创意表达的热门平台。其简便易用的特性和多元创作方式使得TikTok在全球范围内崭露头角。本文将深入探讨TikTok在文化交流中的作用&#xff0c;以及它在全球化表达方面的独特之处…

解决RuntimeError: CUDA error: invalid device ordinal

步骤 首先查看自己设备的cuda版本 #如下linux指令都可以&#xff0c;主要还是以nvidia-smi为主 nvidia-smi nvcc -V用的python版本是3.8 torch版本用的1.12.1cu113 torch网址&#xff1a;https://pytorch.org/get-started/previous-versions/ 安装完后发现出现如下问题&#…

蓝桥杯小白赛第一场(1~6)(期望DP)

1、模拟 2、贪心 3、前缀和 4、猜结论 5、双指针 6、期望DP 1. 蘑菇炸弹 思路&#xff1a;一个简单的暴力模拟。 #include <bits/stdc.h> using namespace std; int main() {int n;cin >> n;vector<int>a(n , 0);for(int i 0 ; i < n ; i )cin &…

深度学习中的各类评价指标

深度学习中的各类评价指标 1 Dice Loss2 Precision&#xff08;精度&#xff09;3 Recall&#xff08;召回率&#xff09;4 F-Score5 mAP 1 Dice Loss Dice Loss&#xff0c;也叫Soft Dice Coefficient&#xff0c;是一种用于图像分割任务的损失函数。它基于目标分割图像与模型…

分库分表,可能真的要退出历史舞台了!

即使是不懂编程的玩家&#xff0c;在对比 NAS 的时候&#xff0c;也会两眼放光&#xff0c;考虑很多因素&#xff0c;比如 RAID 级别、速度、易用程度等。作为时时刻刻与代码打交道的我们&#xff0c;更需要关注数据的存取问题。 一开始&#xff0c;开箱即用的 MySQL&#xff0…

AI一键生成增删改查代码

AI一键生成增删改查代码 在线体验&#xff1a;体验地址 使用教程 1. 描述需求 准确清晰的描述你的需求&#xff0c;如&#xff1a;基于RBAC模型的权限管理系统&#xff0c;点击AI图标 2. AI生成SQL语句 AI将根据你的需求描述生成SQL语句 CREATE TABLE users (id INT A…

高危性行为感染HPV几率有多大?谭巍主任阐述三大要点

高危性行为是指与多个性伴侣发生性行为&#xff0c;或者与性伴侣发生无保护措施的性行为。这些行为增加了感染性传播疾病的风险&#xff0c;包括人乳头瘤病毒(HPV)。 一、HPV感染的风险 1. 性伴侣数量&#xff1a;性伴侣数量越多&#xff0c;感染HPV的几率就越高。与多个性伴…

2023年AMC8数学竞赛真题的典型考点和解析

现在距离2024年1月19日的AMC8数学竞赛还有一个多月的时间&#xff0c;最后一个多月的时间&#xff0c;六分成长建议在前期知识点和内容体系都比较熟悉的基础上&#xff0c;以刷真题为主。同时通过刷真题的查漏补缺&#xff0c;补齐短板。 如何提高刷真题的效率呢&#xff1f;当…

数据结构之----二叉树、二叉树遍历、二叉树数组表示、二叉搜索树

数据结构之----二叉树、二叉树遍历、二叉树数组表示、二叉搜索树 什么是二叉树&#xff1f; 二叉树是一种非线性数据结构&#xff0c;代表着祖先与后代之间的派生关系&#xff0c;体现着“一分为二”的分治逻辑。 与链表类似&#xff0c;二叉树的基本单元是节点&#xff0c;每…

软件测试基础知识总结(超详细整理)

基础篇 1. 什么是软件测试&#xff1f; 软件测试&#xff08;Software Testing&#xff09;的经典定义是&#xff1a;在规定的条件下对程序进行操作&#xff0c;以发现程序错误&#xff0c;衡量软件质量&#xff0c;并对其是否能满足设计要求进行评估的过程。简单来讲就是&am…

【论文翻译】Learning Deep Features for Discriminative Localization

原文&#xff1a;Learning Deep Features for Discriminative Localization 摘要 在这项工作中&#xff0c;我们重新审视了文献[13]中提出的全局平均池化层&#xff0c;并阐明了它如何明确地使卷积神经网络具有出色的定位能力&#xff0c;尽管该网络是在图像级标签上进行训练的…

我的隐私计算学习——隐私集合求交(2)

笔记内容来自多本书籍、学术资料、白皮书及ChatGPT等工具&#xff0c;经由自己阅读后整理而成。 前篇可见&#xff1a;我的隐私计算学习——隐私集合求交&#xff08;1&#xff09; &#xff08;三&#xff09;PSI应用场景问题 ​在目前的实际应用中&#xff0c;衍生出一些新…

【操作系统和计网从入门到深入】(三)进程控制

前言 这个专栏其实是博主在复习操作系统和计算机网络时候的笔记&#xff0c;所以如果是博主比较熟悉的知识点&#xff0c;博主可能就直接跳过了&#xff0c;但是所有重要的知识点&#xff0c;在这个专栏里面都会提到&#xff01;而且我也一定会保证这个专栏知识点的完整性&…

【大数据-Hadoop】从入门到源码编译-概念篇

【大数据-Hadoop】从入门到源码编译-概念篇 Hadoop与大数据生态&#xff08;一&#xff09;Hadoop是什么&#xff1f;&#xff08;二&#xff09;Hadoop组成1. HDFS1.1 NameNode&#xff08;nn&#xff09;1.2 DataNode&#xff08;dn&#xff09;1.3 Secondary NameNode&#…

如雨后春笋般层出不穷的人工智能,究竟可以为我们的生活带来些什么?

似乎是从chatgpt爆火以后&#xff0c;各种各样的和AI、人工智能有关的产品层出不穷&#xff0c;似乎只有带有人工智能&#xff0c;才能体现一个产品的功能之强大&#xff0c;才能在众多产品中具有一定的竞争力&#xff0c;那么这样的现象会给我们的生活带来什么影响呢&#xff…

如何用scratch画正多边形

各边相等&#xff0c;各角也相等的多边形叫做正多边形。 正多边形的外接圆的圆心叫做正多边形的中心。 正多边形的外接圆的半径叫做正多边形的半径。 中心到圆内接正多边形各边的距离叫做边心距。 正多边形各边所对的外接圆的圆心角都相等&#xff0c;这个圆心角叫做正多边…