深入源码解析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) {// 创建一个空的ArrayListArrayList<String> list = new ArrayList<>();// 预先设定ArrayList内部数组的容量为20list.ensureCapacity(20);// 现在,ArrayList的内部数组至少可以容纳20个元素// 添加元素到ArrayListlist.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

盈若安好,便是晴天

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

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

相关文章

【UE】在蓝图中修改材质实例的参数的两种方式

目录 方式一、通过“在材质上设置标量/向量参数值”节点实现 方式二、通过“设置标量/向量参数值”节点实现 方式一、通过“在材质上设置标量/向量参数值”节点实现 1. 在材质中设置了两个参数 2. 创建材质实例 3. 创建一个蓝图&#xff0c;对静态网格体赋予材质实例 在事件…

控制笔记本电脑性能,增强性能/控制发热---Thinkpad x280

1、引言 手上有一台收来办公的Thinkpad x280,但安装的联想管家却没有性能调节选项&#xff0c;导致电脑性能释放很不顺手。由于有室外办公需求&#xff0c;也就有续航需求&#xff0c;也是让它减少发热&#xff1b;同时我想在室内的时候&#xff0c;完整发挥它的性能&#xff…

海思越影系列3516DV500/3519DV500/3519AV200/SD3403平台的AI一体化工业相机设计思路

随着工业自动化的发展&#xff0c;生产线对机器视觉的数量要求越来越多&#xff0c;由于数量的增加&#xff0c;视觉系统占的空间也越来越大&#xff0c;给生产线的布局带来困扰。 另一方面随着视觉SOC的发展&#xff0c;越来越多的视觉SOC都逐渐带有一定的算力&#xff0c;一体…

慢SQL的治理经验

其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、慢SQL导致的后果 二、可能导致慢SQL的原因 三、如何发现慢SQL 3.1 JVM Sandbox 四、识别高危SQL 4.1 阿里的重点强制SQL规…

刚学Python有点难怎么办?这是好事啊!

对于像我一样非计算机专业出身的学生&#xff0c;每当我们想自学一些编程技能的时候&#xff0c;就感觉困难重重&#xff0c;思考坚持下去有没有意义&#xff0c;因此我总结了以下7个小Tips&#xff0c;这些Tips曾经帮助我合理地安排时间&#xff0c;让自学Python的节奏保持起来…

安全算法(一):安全技术、加密的基础知识、哈希函数的简单介绍

安全算法&#xff08;一&#xff09;&#xff1a;安全技术、加密的基础知识、哈希函数的简单介绍 通过互联网交换数据时&#xff0c;数据要经过各种各样的网络和设备才能传到对方那里。数据在传输过程中有可能会经过某些恶意用户的设备&#xff0c;从而导致内容被盗取。 因此…

免费且好用的 MySQL 客户端

DBeaver 支持 Mac、Windows、Linux&#xff0c;提供 Eclipse 插件。社区版免费&#xff0c;支持主流的关系型数据库 官网地址&#xff1a;DBeaver Community | Free Universal Database Tool MySQL WorkBench MySQL WorkBench 是官方出品的客户端&#xff0c;支持 Mac、Windo…

【项目管理】CMMI对项目管理有哪些个人启发和思考

导读&#xff1a;本人作为项目经理参与公司CMMI5级评审相关材料准备工作&#xff0c;现梳理CMMI有关知识点&#xff0c;并结合项目给出部分示例参考&#xff0c;以及本人对于在整理材料过程中一些启发和体验思考。 目录 1、CMMI定义 2、CMMI-5级 3、CMMI文档清单 4、示例-度…

Python实战 | 如何抓取腾讯视频

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 爬虫: 作用: 批量采集数据 / 模拟用户行为 原理: 模拟成 客户端 向 服务器 发送网络请求 环境介绍: python 3.8 解释器 pycharm 编辑器 第三方模块: reques…

【大数据-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&#…

C语言之数组精讲(1)

目录 数组 数组的声明&#xff08;使用数组前的准备&#xff09; 访问数组&#xff08;数组的使用方法&#xff09; 数组的遍历 数组初始化 1.在声明变量时&#xff0c;除了必要的情况下&#xff0c;都需要对变量进行初始化。 2.我们还可以像下面在声明数组时不指定元素…

优思学院|ISO9001:2015七项原则的实际应用

当今&#xff0c;质量管理是一种有效的策略&#xff0c;可以帮助企业组织改善整体业绩&#xff0c;奠定可持续经营的基础。简而言之&#xff0c;质量管理的目标是确保持续不断地追求卓越。 这里我们简单地总结了一下质量管理的七大原则&#xff0c;这些原则都是ISO 9001:2015的…

如何实现nacos的配置的热更新

我们在使用nacos进行修改配置后&#xff0c;需要微服务无需重启即可让配置生效&#xff0c;也就是使配置进行热更新我们可以采用下面的两种方式进行配置的热更新操作 方式一&#xff1a;在Value所注入的变量的类上添加注解RefreshScope RestController RequestMapping("/o…

9:00面试,9:05就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到12月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40…

JVM 内存分析工具 Memory Analyzer Tool(MAT)的深度讲解

目录 一. 前言 二. MAT 使用场景及主要解决问题 三. MAT 基础概念 3.1. Heap Dump 3.2. Shallow Heap 3.3. Retained Set 3.4. Retained Heap 3.5. Dominator Tree 3.6. OQL 3.7. references 四. MAT 功能概述 4.1. 内存分布 4.2. 对象间依赖 4.3. 对象状态 4.4…

飞天使-linux操作的一些技巧与知识点3-http的工作原理

文章目录 http工作原理nginx的正向代理和反向代理的区别一个小技巧dig 命令巧用 http工作原理 http1.0 协议 使用的是短连接&#xff0c;建立一次tcp连接&#xff0c;发起一次http的请求&#xff0c;结束&#xff0c;tcp断开 http1.1 协议使用的是长连接&#xff0c;建立一次tc…

IntelliJ IDEA 自带HTTP Client接口插件上传文件示例

如何使用IntelliJ IDEA自带的HTTP Client接口插件进行文件上传的示例。在这个示例中&#xff0c;我们将关注Controller代码、HTTP请求文件&#xff08;xxx.http&#xff09;&#xff0c;以及文件的上传和处理。 Controller代码 首先&#xff0c;让我们看一下处理文件上传的Co…

建筑学VR虚拟仿真情景实训教学

首先&#xff0c;建筑学VR虚拟仿真情景实训教学为建筑学专业的学生提供了一个身临其境的学习环境。通过使用VR仿真技术&#xff0c;学生可以在虚拟环境中观察和理解建筑结构、材料、设计以及施工等方面的知识。这种教学方法不仅能帮助学生更直观地理解复杂的建筑理论&#xff0…

【Database】什么是数据库?常见的数据库类型有哪些?

什么是数据库&#xff1f;常见的数据库类型有哪些&#xff1f; 首先&#xff0c;什么是数据库&#xff1f;把它想象成一个数字游乐场&#xff0c;我们以结构化的方式组织和存储大量信息。现在&#xff0c;让我们来谈谈数据库的主要类型。 关系型数据库&#xff1a; 想象一下…

【Linux】cat 命令使用

cat 命令 cat&#xff08;英文全拼&#xff1a;concatenate&#xff09;命令用于连接文件并打印到标准输出设备上。 可以使用cat连接多个文件、创建新文件、将内容附加到现有文件、查看文件内容以及重定向终端或文件中的输出。 cat可用于在不同选项的帮助下格式化文件的输出…