Java实现ArrayList和底层源码讲解

news2025/1/22 20:58:21

🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!

人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习!

欢迎志同道合的朋友一起加油喔🦾🦾🦾
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个🐒嘿嘿
谢谢你这么帅气美丽还给我点赞!比个心


目录

一. 模拟实现ArrayList​编辑

1.定义顺序顺序表

2. 函数实现

(1) 打印顺序表display()函数

(2) 新增元素函数add() (默认在数组最后新增)

(3) 在 pos 位置新增元素add()函数(与上方函数构成重载)

(4) 判定是否包含某个元素contains()函数

(5) 查找某个元素对应位置indexOf() 函数

(6) 获取pos位置的元素get()函数

(7) 将pos位置的元素更新为value set()函数

(8) 删除第一个关键字key remove()函数

(9) 获得顺序表的长度size()函数

(10) 清空顺序表clear()函数

 (11) 异常的定义:

3.测试代码

二. ArrayList底层的扩容机制

三.顺序表的优缺点



 顺序表本质上就是一个数组,而顺序表就是实现对这个数组进行增删查改等操作方法的一个类,而Java中也有类似与顺序表的集合类:ArrayList<E> 下面我会对这个类里面比较重要且常用的方法进行实现。

一. 模拟实现ArrayList

我们这里是模拟实现,所以实现基本功能即可

1.定义顺序顺序表

public class MyArrayList{
 
    public int[] elem;
    public int usedSize;//目前存储元素个数
    //默认容量
    private static final int DEFAULT_SIZE = 10;
 
    public MyArrayList() {
        this.elem = new int[DEFAULT_SIZE];
    }
}

上方定义类SeqList即是顺序表,定义elem数组存储数据,定义usedSize表示当前数组中包含多少个元素,定义DEFAULT_SIZE值为了在构造方法中将顺序表初始化为DEFAULT_SIZE大小的数组。

2. 函数实现

(1) 打印顺序表display()函数

 打印函数顾名思义就是将顺序表中所有的数据打印出来。打印完的标准就是将顺序表中的数组完全输出,因此用usedSize作为终止条件,

public void display() {
        for (int i = 0; i < this.usedSize; i++) {
            System.out.print(elem[i]+" ");
        }
        System.out.println();
    }

(2) 新增元素函数add() (默认在数组最后新增)

此函数是增加顺序表中元素的函数。增加元素时,有一点需要注意,你的顺序表是否含有空间放入新的元素。使用函数isFull()判断,若没有满就正常增加,若已满,先对顺序表扩容,再进行增加

 如何判断顺序表满没满?当数组的长度等于数组中包含元素就为满。

当数据载入顺序表成功后,数组中包含元素个数usedSize就需要加1。

public void add(int data) {
        if (this.usedSize == elem.length) {
            this.elem = Arrays.copyOf(elem, elem.length * 2;
        }
        elem[usedSize] = data;
        usedSize++;
    }

(3) 在 pos 位置新增元素add()函数(与上方函数构成重载)

我们要判断传过来的下标的合法性;分析pos的取值范围:我们要把新增函数放到现在已有元素之间,因此我们可以知道,pos的范围应该是 0<=pos<usedSize.若给定的下标不合法,我们就抛出一个异常,让程序停下来。

再者这个函数功能也是添加,因此我们也需要判断顺序表空间的问题。

若我们插入的位置已有元素,我们需把从pos位置的元素向后移,为data数据挪开位置。

当数据载入顺序表成功后,数组中包含元素个数usedSize就需要加1。

public void add(int pos, int data) {
        if (pos < 0 || pos > usedSize) {
            throw new ArrayIndexException("下标非法,请检查");
        }
        if (this.usedSize == elem.length) {
            this.elem = Arrays.copyOf(elem, elem.length * 2;
        }
        for (int i = usedSize - 1; i >= pos; i--) {
            elem[i + 1] = elem[i];
        }
        elem[pos] = data;
        usedSize++;
    }

 下面动图演示:

(4) 判定是否包含某个元素contains()函数

 拿到需要找的数据toValue,遍历数组,将数组中的所有值与toValue进行比较,若数组中的值有与toValue相等,那么就返回true,否则返回false。

public boolean contains(int toValue) {
        for (int i = 0; i < usedSize; i++) {
            if (elem[i] == toValue) {
                return true;
            }
        }
        return false;
    }

(5) 查找某个元素对应位置indexOf() 函数

同上方思想一致,拿到需要找的数据toValue,遍历数组,将数组中的所有值与toValue进行比较,若数组中的值有与toValue相等,那么就返回下标i,否则返回-1。

public int indexOf(int toValue) {
        for (int i = 0; i < usedSize; i++) {
            if (elem[i] == toValue) {
                return i;
            }
        }
        return -1;
    }

(6) 获取pos位置的元素get()函数

我们要判断下标是否合法,我们需要顺序表中的元素,因此pos不能超过顺序表中第一个元素的下标和最后一个元素的下标,即下标是0<=pos<usedSize,如果不在这个范围内,我们抛出一个异常,让程序停下来。

public int get(int pos) {
        if(pos<0||pos>=usedSize){
            throw new ArrayIndexException("下标非法,请查看!");
        }
        return elem[pos];
    }

(7) 将pos位置的元素更新为value set()函数

同上方一样,我们要判断下标是否合法,我们需要顺序表中的元素,因此pos不能超过顺序表中第一个元素的下标和最后一个元素的下标,即下标是0<=pos<usedSize,如果不在这个范围内,我们抛出一个异常,让程序停下来。最后将value赋到pos位置

public void set(int pos, int value) {
        if(pos<0||pos>=usedSize){
            throw new ArrayIndexException("下标错误,请查看!");
        }
        elem[pos] = value;
    }

(8) 删除第一个关键字key remove()函数

我们先调用indexOf()函数,判断顺序表中是否有我们删除的值,若有找到这个值的下标,没有就返回false。有就以覆盖的方式,将index下标的元素用他后一个元素覆盖,一直往复到最后一个元素,因为删除了一个元素,所以将usedSize减一,因为在覆盖时,最后一个元素是无效数据所以没有覆盖,我们将他置为0。

public boolean remove(int key) {
        int index = indexOf(key);
        if (index == -1) {
            System.out.println("没有这个数据");
            return false;
        }
        for (int i = index; i < usedSize - 1; i++) {
            elem[i] = elem[i + 1];
        }
        usedSize--;
        elem[usedSize] = 0;
        return true;
    }

 下面动图演示:

(9) 获得顺序表的长度size()函数

当前数组中包含多少个元素,顺序表就是多长,因此顺序表的长度就是usedSize的大小。

public int size() {
        return usedSize;
    }

(10) 清空顺序表clear()函数

因为所有的函数都是围绕这usedSisz进行构造的,我们只需将usedSize置为0,其他函数就无法进行运行(此处并非范例)

public void clear() {
        usedSize = 0;
    }

 (11) 异常的定义:

public class ArrayIndexException extends RuntimeException{
 
 
        public ArrayIndexException() {
        }
 
        public ArrayIndexException(String message) {
            super(message);
        }
}

3.测试代码



public class Test {
    public static void main(String[] args) {
        MyArrayList MyArrayList =new MyArrayList();
        //添加数据
        MyArrayList.add(1);
        MyArrayList.add(2);
        MyArrayList.add(3);
        MyArrayList.add(4);
        MyArrayList.add(5);
        //打印
        System.out.print("当前数组元素:");
        MyArrayList.display();
        //值所在的下标
        System.out.print("值所在的下标:");
        System.out.println(MyArrayList.indexOf(3));
        //是否包含这个值
        System.out.print("是否包含这个值:");
        System.out.println(MyArrayList.contains(2));
        //获得下标所在位置元素
        System.out.print("获得下标所在位置元素:");
        System.out.println(MyArrayList.get(3));
        //修改下标的值
        System.out.print("修改下标的值:");
        MyArrayList.set(3,12);
        MyArrayList.display();
        //删除关键字key
        System.out.print("删除关键字key:");
        MyArrayList.remove(2);
        MyArrayList.display();
        //获得长度
        System.out.print("获得当前顺序表长度:");
        System.out.println(MyArrayList.size());
    }
}

输出结果如下: 


二. ArrayList底层的扩容机制

ArrayList是一个动态类型的顺序表,即在插入元素的时候会自动扩容,下面是扩容机制:

🕯️总结:

✨当使用无参的构造方法创建ArrayList,容量默认值10在第一次插入的时候才会初始化初始容量,而不是在创建的时候就初始化容量

✨在插入时,会检测是否真正需要扩容,如果需要,调用grow扩容

✨初步预估按照原容量的1.5倍扩容
✨如果用户所需大小超过预估的1.5倍,则按照用户所需大小扩容
✨真正扩容之前检测是否能扩容成功,防止太大导致扩容失败

✨使用copyOf进行扩容 

三.顺序表的优缺点

 顺序表也是分情况使用的,如何判断呢?总结了它的优缺点,可以作为参考。

  优点:

  1. 无需为表示表中元素之间的逻辑关系而增加额外的存储空间。
  2. 可以快速地获取表中任意位置的元素。

  缺点:

  1. 插入和删除操作需要移动大量元素(时间复杂度高)。
  2. 当线性表长度变化较大时,难以确定存储空间的容量。
  3. 造成存储空间的“碎片”。(当我们添加元素时,例如我们有101个元素,数组初始大小为100,扩容为1.5倍,初始大小无法存储全部元素,因此需要扩容,扩容为150大小,这就浪费了49个空间)

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

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

相关文章

Centos7安装MySQL5.7.30

文章目录1. 环境准备1.1 卸载mariadb1.2 下载MySQL 5.7.301.3 安装MySQL依赖项1.4 创建目录1.5 创建用户和用户组1.6 修改Mysql用户权限2. 安装MySQL2.1 解压2.2 修改解压目录名称2.3 初始化2.4 添加my.cnf异常找不到Sock文件2.5 启动MySQL服务2.5.1 建立软连接2.5.2 启动2.6 设…

TCP协议工作机制二(滑动窗口,流量控制,拥塞控制,延时应答,捎带应答等)

目录 滑动窗口 流量控制 拥塞控制 延时应答 捎带应答 面向字节流 异常情况 UDP和TCP对比 滑动窗口 由于TCP是可靠传输,有确认应答,超时重传,连接管理等机制,发送消息时需要等待接收方返回的ack.因此会消耗大量等待ack的时间,我们引入滑动窗口的机制来竭尽可能提高TCP的…

基于支持向量机的Digits手写数字识别

基于支持向量机的Digits手写数字识别 描述 支持向量机&#xff08;Support Vector Machine&#xff0c;简称SVM&#xff09;模型既可以用于分类也可以用于回归。手写数字识别是一个多分类问题&#xff08;判断一张手写数字图片是0~9中的哪一个&#xff09;&#xff0c;数据集…

图片英文翻译成中文转换器-中文翻译英文软件

您正在准备一份重要的英文资料或文件&#xff0c;但是您还不是很熟练地掌握英文&#xff0c;需要翻译才能完成您的任务吗&#xff1f;哪个软件能够免费把英文文档翻译成中文&#xff1f;让我们带您了解如何使用我们的翻译软件来免费翻译英文文档为中文。 我们的翻译软件是一款功…

C风格的字符串赋值方式

文章目录&#xff08;1&#xff09;C语言中&#xff0c;没有字符串类型但可以用字符数组模拟字符串。&#xff08;2&#xff09;C语言中&#xff0c;字符串是以’\0’作结尾字符。&#xff08;3&#xff09;C语言中&#xff0c;字符串常量本质上是一个无名的字符数组。C风格的字…

使用Spring JDBC中的JdbcTemplate对数据进行增删改查操作教程~

jdbcTemplate实现添加数据功能&#xff1a; spring框架对jdbc框架进行封装&#xff0c;使用jdbcTemplate方便实现对数据库的操作 数据库准备工作&#xff1a; 在已有数据库中创建新的表&#xff1a; create table t_user (id int,username varchar(20),password varchar(20…

搜索词分析工具-网站关键词挖掘

怎么能找到行业的关键词 以下是如何找到行业关键词的建议&#xff1a; 了解行业&#xff1a;要找到与行业相关的关键词&#xff0c;首先需要了解行业。了解行业以及核心目标&#xff0c;从而更好地理解行业中的主题和词汇。 找到竞争对手网站&#xff1a;搜索竞争对手的网站&…

k8s部署Dashboard

k8s和Dashboard的版本对应关系可以到Dashbord的对应版本里看&#xff0c;比如这里&#xff1a; https://github.com/kubernetes/dashboard/releases/tag/v2.7.0 以下步骤都是在master上执行的。 1. 部署步骤 1. 获取Dashbord的yaml文件 wget https://raw.githubusercontent…

【Git】—— 如何安装Git及简单使用

Git是一个开源的分布式版本控制工具&#xff0c;可以更好地管理你的项目。 一、Linux操作系统 如果用的是Ubuntu系统&#xff0c;只需打开shell界面&#xff0c;输入&#xff1a; sudo apt-get install git-core 按下回车即可完成安装。 二、Windows操作系统 Windows操作系统不…

C语言-数据结构与算法-详细全面的链表知识总结归纳

C语言链式存储结构的详细讲解一.前言(为什么要使用链式存储)一.单链表1.单链表的结点描述2.单链表基本操作(1)初始化单链表(2)采用头插法建立单链表(带头结点)(3).采用尾插法建立单链表(4)按照位序查找结点(4)在链表中间插入结点(5)删除第i个结点二.双链表1.双链表的结点类型描…

和ChatGPT-4聊完后,我觉得一切可能已经来不及了

了然无味&#xff0c;晴空万里&#xff01;和ChatGPT-4开始了一场坦诚的沟通&#xff0c;它全程都表现出高情商&#xff0c;以及不断尽量安抚我的情绪&#xff0c;而这&#xff0c;恰恰令我脊背发凉。 部分文字截取 ZM&#xff1a;我能不能理解每次对话就是一次你的“生命” G&…

【Android -- 软技能】分享一个学习方法

前言 很多人都想通过学习来提升自己&#xff0c;但是&#xff0c;可能因为两个问题&#xff0c;阻碍了自己的高效提升&#xff1a; 学什么&#xff1f; 怎么学&#xff1f; 本文将从自己的学习实践出发&#xff0c;针对这两个问题&#xff0c;给出自己的一套学习流程。 1…

免费集装箱号识别API免费集装箱信息识别,中国人工智能企业CIMCAI集装箱识别云服务全球4千企业用户,中国人工智能企业智慧港航

免费集装箱号识别API免费集装箱信息识别API&#xff0c;CIMCAI飞瞳引擎™集装箱人工智能平台全球4千企业用户&#xff0c;全球领先的飞瞳引擎™AI集装箱识别云服务&#xff0c;集装箱残损识别箱况检测缺陷检验&#xff0c;小程序拍照检测或支持API接口二次开发&#xff0c;应用…

00后整顿职场,我直呼太卷了....

内卷的来源 内卷最早的“出处”是几张名校学霸的图片。 大学生们刷爆朋友圈的几张“内卷”图片是这样的&#xff1a;有的人骑在自行车上看书&#xff0c;有的人宿舍床上铺满了一摞摞的书&#xff0c;有的人甚至边骑车边端着电脑写论文。这些图片最早在清华北大的学霸之间流传。…

AI工具究竟是帮手还是对手?对此你怎么看,一起来聊聊你的看法吧!

© Ptw-cwl 前言 AI工具既可以是帮手&#xff0c;也可以是对手&#xff0c;这取决于我们如何使用它们。 如果我们正确地利用AI工具&#xff0c;它们可以为我们带来很多好处&#xff0c;例如更快的数据分析、更准确的预测和更高效的决策。然而&#xff0c;如果我们滥用AI工…

嵌入式开发:硬件和软件越来越接近

从前&#xff0c;硬件和软件工程师大多生活在自己的世界里。硬件团队设计了芯片&#xff0c;调试了从铸造厂返回的第一批样本&#xff0c;让软件团队测试他们的代码。随着虚拟平台和其他可执行模型变得越来越普遍&#xff0c;软件团队可以在芯片制造之前开始&#xff0c;有时甚…

贝叶斯优化 | BO-RF贝叶斯优化随机森林多输入单输出回归预测(Matlab完整程序)

贝叶斯优化 | BO-RF贝叶斯优化随机森林多输入单输出回归预测(Matlab完整程序) 目录 贝叶斯优化 | BO-RF贝叶斯优化随机森林多输入单输出回归预测(Matlab完整程序)预测结果基本介绍评价指标程序设计参考资料预测结果 基本介绍 贝叶斯优化 | BO-RF贝叶斯优化随机森林多输入单…

面试题

用 C写一个函数&#xff0c;交换两个整型变量 int a 5, b 10; cout << "Before swapping: a " << a << ", b " << b << endl; swapVars<int>(a, b); cout << "After swapping: a " << a …

半透明反向代理 (基于策略路由)

定义 半透明反向代理一般是指 代理本身对于客户端透明&#xff0c;对于服务端可见。 从客户端视角看&#xff0c;客户端访问的还是服务端&#xff0c;客户端不知道代理的存在。 从服务端视角看&#xff0c;服务端只能看到代理&#xff0c;看不到真实的客户端。 示意图 客户端…

【C语言】switch语句的理解

文章目录一. 基本语法结构二. 几点补充补充一&#xff1a;关于 default 分支补充二&#xff1a;多条匹配执行同一语句补充三&#xff1a;在 case 语句中定义变量的问题三. 几点建议建议一&#xff1a;按执行频率排列 case 语句细节二&#xff1a;简化每种情况对应的操作细节三&…