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

news2024/11/24 14:11:06

文章目录

    • 一、 简介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

盈若安好,便是晴天

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

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

相关文章

OLED屏幕,如何成为商显主流

OLED屏幕在商显领域的应用逐渐增加&#xff0c;成为商显主流的原因主要有以下几点&#xff1a; 显示效果优异&#xff1a;OLED屏幕具有自发光原理&#xff0c;色彩鲜艳、对比度高、视角广&#xff0c;能够提供更好的视觉体验。在商业展示、广告宣传等场景中&#xff0c;OLED屏幕…

高通平台开发系列讲解(USB篇)Composite USB gadget framework

文章目录 一、Gadget framework二、Composite driver and gadget driver interaction沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇章主要图解高通平台PCIe EP软件架构 一、Gadget framework Composite USB gadget framework 架构如下所示: The composite fram…

【二分查找】【区间合并】LeetCode2589:完成所有任务的最少时间

作者推荐 【动态规划】【广度优先】LeetCode2258:逃离火灾 本文涉及的基础知识点 二分查找算法合集 有序向量的二分查找&#xff0c;向量只会在尾部增加删除。 题目 你有一台电脑&#xff0c;它可以 同时 运行无数个任务。给你一个二维整数数组 tasks &#xff0c;其中 ta…

java常用集合的区别与联系以及应用场景

文章目录 Java集合三大类&#xff08;1&#xff09;List概述&#xff08;2&#xff09;Set概述&#xff08;3&#xff09;Map概述 集合间的区别与联系List&#xff0c;Set和Map的区别ArrayList、Vector和LinkedList的区别HashSet、LinkedHashSet和TreeSet的区别HashSet与HashMa…

探索未来新趋势:鸿蒙系统的崭新时代

探索未来新趋势&#xff1a;鸿蒙系统的崭新时代 随着科技的不断发展&#xff0c;操作系统作为计算机和移动设备的核心&#xff0c;扮演着至关重要的角色。近年来&#xff0c;一种备受瞩目的操作系统——鸿蒙系统&#xff08;HarmonyOS&#xff09;崭露头角&#xff0c;正引领着…

Standoff 12 网络演习

在 11 月 21 日至 24 日于莫斯科举行的 "Standoff 12 "网络演习中&#xff0c;Positive Technologies 公司再现了其真实基础设施的一部分&#xff0c;包括软件开发、组装和交付的所有流程。安全研究人员能够在安全的环境中测试系统的安全性&#xff0c;并尝试将第三方…

Java - Spring中BeanFactory和FactoryBean的区别

BeanFactory Spring IoC容器的顶级对象&#xff0c;BeanFactory被翻译为“Bean工厂”&#xff0c;在Spring的IoC容器中&#xff0c;“Bean工厂”负责创建Bean对象。 BeanFactory是工厂。 FactoryBean FactoryBean&#xff1a;它是一个Bean&#xff0c;是一个能够辅助Spring实例…

Qt 中文处理

windows下 Qt显示中文的几种方式&#xff1a; 1&#xff0c; 环境&#xff1a;Qt 5.15.2 vs2019 64位 win11系统 默认用Qt 创建的文件使用utf-8编码格式&#xff0c;此环境下 中文没有问题 ui->textEdit->append("中文测试"); 2&#xff0c; 某些 低于…

js Array.every()的使用

2023.12.13今天我学习了如何使用Array.every()的使用&#xff0c;这个方法是用于检测数组中所有存在的元素。 比如我们需要判断这个数组里面的全部元素是否都包含张三&#xff0c;可以这样写&#xff1a; let demo [{id: 1, name: 张三}, {id: 2, name: 张三五}, {id: 3, name…

高效数组处理的Numpy入门总结

NumPy是Python中一个重要的数学库&#xff0c;它提供了高效的数组操作和数学函数&#xff0c;是数据科学、机器学习、科学计算等领域的重要工具。下面是一个简单的NumPy学习教程&#xff0c;介绍了NumPy的基本用法和常用函数。 安装NumPy 在使用NumPy之前&#xff0c;需要先安…

ArkTS的状态管理机制(State)

什么是ArkTS的状态管理机制 声明式UI中&#xff0c;是以状态(State)来驱动视图更新的(View)。 状态是指驱动视图更新的数据(被装饰器标记的变量)。 视图是指UI描述渲染得到的用户页面。 互动事件可以改变状态的值。状态改变以后&#xff0c;又会触发事件&#xff0c;渲染页面。…

统信UOS使用4种方法重置用户密码

原文链接&#xff1a;统信UOS使用4种方法重置用户密码 hello&#xff0c;大家好啊&#xff0c;今天我要给大家介绍的是在统信UOS操作系统上使用4种不同方法来重置用户密码。我们都知道&#xff0c;在日常使用中&#xff0c;偶尔会忘记密码&#xff0c;尤其是在使用多个账户的情…

蓝牙协议栈学习笔记

蓝牙协议栈学习笔记 蓝牙简介 蓝牙工作在全球通用的 2.4GHz ISM&#xff08;即工业、科学、医学&#xff09;频段&#xff0c;使用 IEEE802.11 协议 蓝牙 4.0 是迄今为止第一个蓝牙综合协议规范&#xff0c;将三种规格集成在一起。其中最重要的变化就是 BLE&#xff08;Blue…

淘宝订单API接口在电商行业中的应用与实现

一、引言 随着电商行业的快速发展&#xff0c;订单处理成为电商运营的核心环节。淘宝作为中国最大的电商平台之一&#xff0c;其订单API接口在电商行业中的应用越来越广泛。本文将详细介绍淘宝订单API接口在电商行业中的应用&#xff0c;并深入剖析相关的技术细节&#xff0c;…

前端设计模式之旅:命令模式

引言 使用命令模式&#xff0c;我们可以将执行特定任务的对象与调用该方法的对象解耦。 核心思想 命令模式的核心思想是将请求封装成一个对象&#xff0c;从而使请求的发起者和请求的执行者解耦。 这样&#xff0c;请求的发起者只需要知道如何创建命令对象并将其传递给请求者…

基于SSM架构的超市管理系统设计

基于SSM架构的超市管理系统设计 目录 基于SSM架构的超市管理系统设计 1 环境及工具1.1 IDEA软件安装1.2 JDK环境配置1.3 MySQL数据库安装1.3.1常规情况1.3.2非常规情况 1.4 Tomcat安装 2 部署与设计2.1 数据库信息创建2.2项目创建与部署 3 相关说明4 功能操作说明4.1 管理员操作…

ORA-600 [2662] “Block SCN is ahead of Current SCN“

ORA-600 [2662] "Block SCN is ahead of Current SCN" (Doc ID 28929.1)​编辑To Bottom Note: For additional ORA-600 related information please read Note:146580.1PURPOSE: This article discusses the internal error "ORA-600 [2662]"…

基于Java技术的会员制度管理的商品营销系统的设计与实现论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本商品营销系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信息&am…

anolisos8.8安装显卡+CUDA工具+容器运行时支持(containerd/docker)+k8s部署GPU插件

anolisos8.8安装显卡及cuda工具 一、目录 1、测试环境 2、安装显卡驱动 3、安装cuda工具 4、配置容器运行时 5、K8S集群安装nvidia插件 二、测试环境 操作系统&#xff1a;Anolis OS 8.8 内核版本&#xff1a;5.10.134-13.an8.x86_64 显卡安装版本&#xff1a;525.147.05 c…

生物芯片市场分析:预计2029年将达到180亿美元

生物芯片(biochip或bioarray)是根据生物分子间特异相互作用的原理&#xff0c;将生化分析过程集成于芯片表面&#xff0c;从而实现对DNA、RNA、多肽、蛋白质以及其他生物成分的高通量快速检测。狭义的生物芯片概念是指通过不同方法将生物分子(寡核苷酸、cDNA、genomic DNA、多肽…