数据结构之ArrayList

news2025/6/3 21:17:33

系列文章目录


目录

系列文章目录

前言

一、数据结构的前置语法

1. 时空复杂度

2. 包装类

3. 泛型

二、ArrayList 和顺序表

1. 顺序表的模拟实现

2. 源码

3. ArrayList 的优缺点


前言

本文介绍数据结构的前置算法,以及 ArrayList 的模拟实现,部分源码及优缺点概括。


一、数据结构的前置语法

1. 时空复杂度

时间复杂度和空间复杂度:用来衡量算法的效率(时间和空间),通常用大O渐进法进行表示;

 常见的时间复杂度:O(1), O(logN), O(N), O(N*logN), O(N^2);

2. 包装类

解释下面代码的原理:

Integer a = 100;
Integer b = 100;

System.out.println(a == b); // 输出 true

Integer c = 200;
Integer d = 200;

System.out.println(c == d); // 输出 false

valueOf(): Integer 源码:

public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
}

Integer.Cache.high = 127, Integer.Cache.low = -128

cache数组中在 [0, 255] 空间中存的是 -128 ~127;

如果数字 i 的值在 [-128, 127] 之间,返回 cache 数组的对应下标 [0, 255] 区间的值,即 -128 ~ 127;否则则会 new 一个新的引用变量;

因此当 i 在 [-128, 127] 之间,返回的都是同一个哈希值,即引用变量相等,否则返回不同的哈希值,即引用变量不相等;

3. 泛型

类名后面写一个 "<T>" ,表示这是一个泛型类;

"<>" 只能存放包装类型,不能存放基本类型;

编译的时候会把 T 类型都替换成 Object 类型,这称为泛型的擦除机制;

泛型的上界:

泛型类:class 类名<T extends XXX> ,XXX 就是泛型的上界;

泛型方法 :<T> 返回类型 方法名<T extends XXX>(){...}, XXX 就是泛型的上界;

二、ArrayList 和顺序表

顺序表底层是一个数组,是使用数组来完成的一种结构;

实现了 List 接口,通常使用 "List<T> list = new ArrayList<>() " 这种向上转型的形式去定义顺序表;

1. 顺序表的模拟实现

下面模拟实现顺序表,理解顺序表的底层原理:

定义一个数组 elem,用于存放元素,定义一个 usedSize 用于表示存放了几个元素;

定义一个常数 DEFAULT_SIZE,用来表示新建一个顺序表默认开辟空间的大小;

定义一个无参的构造方法,默认开辟的空间大小为 DEFAULT_SIZE;

定义一个有一个参数的构造方法,参数用于指定开辟空间的大小;

public class MyArrayList {
    private int[] elem;
    private int usedSize;
    private static final int DEFAULT_SIZE = 10;

    public MyArrayList(){
        this.elem = new int[DEFAULT_SIZE];
    }

    public MyArrayList(int capacity){
        this.elem = new int[capacity];
    }

    // 方法的位置
    // ......
}

下面实现顺序表的方法(增删改查):

打印顺序表的所有元素:

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

向顺序表中增加一个元素:

注意:在顺序表中增加元素的时候,需要先判断当前是否已经存满,如果存满了要先进行扩容才可以再往顺序表中添加元素;

isFull(): boolean 判断顺序表是否存满;每次增加元素都需要判断顺序表是否存满;

add(int data): void 向顺序表最后一个位置新增一个元素;

add(int pos, int data): void 向顺序表的 pos 位置新增一个元素;

需要判断 pos 位置是否合法,不合法要抛出异常;

如果合法,就要将 pos 位置以及 pos 后面的元素,往后挪一个位置,考虑到可能覆盖后一个元素的问题,需要从后往前依次向后挪每个元素;

增加元素一定不要忘记 usedSize++;

    // 判断是否满
    public boolean isFull(){
        if(this.usedSize == this.elem.length) {
            return true;
        }
        return false;
    }

    // 新增元素,默认在数组最后新增
    public void add(int data){
        if(isFull()){
            // 扩容
            this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
        }
        this.elem[usedSize] = data;
        usedSize++;
    }
    
    // 在 pos 位置新增元素
    public void add(int pos, int data){
        if(pos < 0 || pos > this.usedSize){
            throw new PosOutOfBoundsException(pos + "位置不合法");
        }
        if(isFull()){
            // 扩容
            this.elem = Arrays.copyOf(this.elem, 2 * this.elem.length);
        }
        for(int i = usedSize - 1; i >= pos; i--){
            this.elem[i + 1] = this.elem[i];
        }
        this.elem[pos] = data;
        this.usedSize++;
    }

查找元素:

contains(int toFind): boolean 判断元素 toFind 是否在顺表中存在;

indexOf(int toFind): int 找元素 toFind 的下标,找到则返回对应下标,找不到返回 -1;

checkPos(int pos): void 判断下标是否合法,查找某个下标元素需要先判断下标是否合法,不合法直接抛出异常;

get(int pos): int 获取 pos 位置元素的下标;需要先判断给定的 pos 下标是否合法;

    // 判断是否包含某个元素
    public boolean contains(int toFind){
        for(int i = 0; i < this.usedSize; i++){
            // 如果是引用类型,可以使用 equals 方法
            if(this.elem[i] == toFind){
                return true;
            }
        }
        return false;
    }

    // 查找某个元素对应的位置
    public int indexOf(int toFind){
        for(int i = 0; i < this.usedSize; i++){
            if(this.elem[i] == toFind){
                return i;
            }
        }
        return -1;
    }

    // 判断下标是否合法    
    private void checkPos(int pos){
        if(pos < 0 || pos >= this.usedSize){
            throw new PosOutOfBoundsException(pos + "位置不合法");
        }
    }

    // 获取某个位置的元素
    public int get(int pos){
        checkPos(pos);
        return this.elem[pos];
    }

修改某个位置元素:

set(int pos, int value): void 修改 pos 位置的元素为 value,修改前同样需要判断 pos 位置是否合法;

    // 给 pos 位置的元素设为 value
    public void set(int pos, int value){
        checkPos(pos);
        this.elem[pos] = value;
    }

删除某个元素:

remove(int toRemove): void 删除元素 toRemove;

先查找该元素的下标;

如果元素不存在直接返回;

如果元素存在则删除该元素,并将该元素后面的元素都往前移一个位置;如果是引用类型的数据,一定记得把最后一个引用置 null;

删除元素一定不能忘记 usedSize--;

    // 删除第一次出现的关键字 key
    public void remove(int toRemove){
        int index = indexOf(toRemove);
        if(index == -1){
            return;
        }
        for(int i = index; i < this.usedSize - 1; i++){
            this.elem[i] = this.elem[i + 1];
        }
        // 如果是引用类型,最后一个位置就要置 null
        // this.elem[this.usedSize - 1] = null
        this.usedSize--;
    }

求顺序表长度:

size(): int 求顺序表长度,直接返回 usedSize 即可;

    // 获取顺序表长度
    public int size(){
        return this.usedSize;
    }

清空顺序表:

clear(): void 清空顺序表;

如果是基本类型,直接将 usedSize 置 0 即可;

如果是引用类型,需要遍历一遍顺序表,将每个引用都置 null

    // 清空顺序表
    public void clear(){
        // 如果是引用类型,就要遍历置为 null
//        for(int i = 0; i < this.usedSize; i++){
//            this.elem[i] = null;
//        }
        this.usedSize = 0;
    }

2. 源码

构造方法:

ArrayList(int initialCapacity):一个参数的构造方法,initialCapacity 用于指定顺序表的空间;

ArrayList():无参的构造方法,并没有分配内存,第一次增加元素的时候才会扩容;

ArrayList(Collection<? extends E> c):可以传 E 类型的子类或者 E 类型本身的 实现了Collection 接口的数据结构;除了 Map 没实现, Collection,List,Queue,Set 都实现了;

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

    /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

ArrayList 扩容机制:

    Object[] elementData;   // 存放元素的空间
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};  // 默认空间
    private static final int DEFAULT_CAPACITY = 10;  // 默认容量大小
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
        grow(minCapacity);
    }
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    private void grow(int minCapacity) {
        // 获取旧空间大小
        int oldCapacity = elementData.length;
        // 预计按照1.5倍方式扩容
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 如果用户需要扩容大小 超过 原空间1.5倍,按照用户所需大小扩容
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        // 如果需要扩容大小超过MAX_ARRAY_SIZE,重新计算容量大小
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 调用copyOf扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    private static int hugeCapacity(int minCapacity) {
        // 如果minCapacity小于0,抛出OutOfMemoryError异常
        if (minCapacity < 0)
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
    }

3. ArrayList 的优缺点

优点:可以通过下标进行随机访问,时间复杂度 O(1);

缺点:

往某个位置添加元素,需要往后移动后面的元素;

删除某个元素,需要移动把后面的元素往前移动;

扩容的时候,每次都需要拷贝所有元素;扩容之后可能会浪费空间;

总结:适合静态的数据查找和更新,不适合插入和删除数据。

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

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

相关文章

DDR4读写压力测试

1.1测试环境 1.1.1整体环境介绍 板卡&#xff1a; pcie-403板卡 主控芯片&#xff1a; Xilinx xcvu13p-fhgb2104-2 调试软件&#xff1a; Vivado 2018.3 代码环境&#xff1a; Vscode utf-8 测试工程&#xff1a; pcie403_user_top 1.1.2硬件介绍 UD PCIe-403…

编写测试用例

测试用例&#xff08;Test Case&#xff09;是用于测试系统的要素集合 目录 编写测试用例作用 编写测试用例要包含七大元素 测试用例的设计方法 1、等价类法 2、边界值法 3、正交表法 4、判定表法 5、错误推测法 6、场景法 编写测试用例作用 1、确保功能全面覆盖…

每日Prompt:隐形人

提示词 黑色棒球帽&#xff0c;白色抹胸、粉色低腰短裙、白色襪子&#xff0c;黑色鞋子&#xff0c;粉紅色背包&#xff0c;衣服悬浮在空中呈现动态姿势&#xff0c;虚幻引擎渲染风格&#xff0c;高清晰游戏CG质感&#xff0c;户外山林背景&#xff0c;画面聚焦在漂浮的衣服上…

TensorFlow深度学习实战(19)——受限玻尔兹曼机

TensorFlow深度学习实战&#xff08;19&#xff09;——受限玻尔兹曼机 0. 前言1. 受限玻尔兹曼机1.1 受限玻尔兹曼机架构1.2 受限玻尔兹曼机的数学原理 2. 使用受限玻尔兹曼机重建图像3. 深度信念网络小结系列链接 0. 前言 受限玻尔兹曼机 (Restricted Boltzmann Machine, RB…

告别手动绘图!基于AI的Smart Mermaid自动可视化图表工具搭建与使用指南

以下是对Smart Mermaid的简单介绍&#xff1a; 一款基于 AI 技术的 Web 应用程序&#xff0c;可将文本内容智能转换为 Mermaid 格式的代码&#xff0c;并将其渲染成可视化图表可以智能制作流程图、序列图、甘特图、状态图等等&#xff0c;并且支持在线调整、图片导出可以Docke…

【Oracle】安装单实例

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 安装前的准备工作1.1 硬件和系统要求1.2 检查系统环境1.3 下载Oracle软件 2. 系统配置2.1 创建Oracle用户和组2.2 配置内核参数2.3 配置用户资源限制2.4 安装必要的软件包 3. 目录结构和环境变量3.1 创建Ora…

QT中更新或添加组件时出现“”qt操作至少需要一个处于启用状态的有效资料档案库“解决方法”

在MaintenanceTool.exe中点击下一步 第一个&#xff1a; 第二个&#xff1a; 第三个&#xff1a; 以上任意一个放入资料库中

论文速读《UAV-Flow Colosseo: 自然语言控制无人机系统》

论文链接&#xff1a;https://arxiv.org/abs/2505.15725项目主页&#xff1a;https://prince687028.github.io/UAV-Flow/ 0. 简介 近年来&#xff0c;无人机技术蓬勃发展&#xff0c;但如何让无人机像智能助手一样理解并执行人类语言指令&#xff0c;仍是一个前沿挑战。现有研…

ES6+中Promise 中错误捕捉详解——链式调用catch()或者async/await+try/catch

通过 unhandledrejection 捕捉未处理的 Promise 异常&#xff0c;手动将其抛出&#xff0c;最终让 window.onerror 捕捉&#xff0c;从而统一所有异常的处理逻辑 规范代码&#xff1a;catch&#xff08;onRejected&#xff09;、async...awaittry...catch 在 JavaScript 的 Pro…

解常微分方程组

Euler法 function euler_method % 参数设置 v_missile 450; % 导弹速度 km/h v_enemy 90; % 敌艇速度 km/h % 初始条件 x0 0; % 导弹初始位置 x y0 0; % 导弹初始位置 y xe0 120; % 敌艇初始位置 y t0 0; % 初始时间 % 时间步长和总时间 dt 0.01; % 时间步长 t_final …

C++实现汉诺塔游戏自动完成

目录 一、汉诺塔的规则二、数学递归推导式三、步骤实现(一)汉诺塔模型(二)递归实现(三)显示1.命令行显示2.SDL图形显示 四、处理用户输入及SDL环境配置五、总结六、源码下载 一、汉诺塔的规则 游戏由3根柱子和若干大小不一的圆盘组成&#xff0c;初始状态下&#xff0c;所有的…

pikachu靶场通关笔记07 XSS关卡03-存储型XSS

目录 一、XSS 二、存储型XSS 三、源码分析 四、渗透实战 1、输入mooyuan试一试 2、注入Payload 3、查看数据库 4、再次进入留言板页面 本系列为通过《pikachu靶场通关笔记》的XSS关卡(共10关&#xff09;渗透集合&#xff0c;通过对XSS关卡源码的代码审计找到XSS风险的…

OpenCV CUDA模块直方图计算------用于在 GPU 上执行对比度受限的自适应直方图均衡类cv::cuda::CLAHE

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::CLAHE 是 OpenCV 的 CUDA 模块中提供的一个类&#xff0c;用于在 GPU 上执行对比度受限的自适应直方图均衡&#xff08;Contrast Limi…

华为OD机试真题——矩形绘制(2025A卷:200分)Java/python/JavaScript/C/C++/GO最佳实现

2025 A卷 200分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…

JDBC连不上mysql:Unable to load authentication plugin ‘caching_sha2_password‘.

最近为一个spring-boot项目下了mysql-9.3.0&#xff0c;结果因为mysql版本太新一直报错连不上。 错误如下&#xff1a; 2025-06-01 16:19:43.516 ERROR 22088 --- [http-nio-8080-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispat…

MacOS安装Docker Desktop并汉化

1. 安装Docker Desktop 到Docker Desktop For Mac下载对应系统的Docker Desktop 安装包&#xff0c;下载后安装&#xff0c;没有账号需要注册&#xff0c;然后登陆即可。 2. 汉化 前往汉化包下载链接下载对应系统的.asar文件 然后将安装好的文件覆盖原先的文件app.asar文件…

Centos系统搭建主备DNS服务

目录 一、主DNS服务器配置 1.安装 BIND 软件包 2.配置主配置文件 3.创建正向区域文件 4.创建区域数据文件 5.检查配置语法并重启服务 二、从DNS服务配置 1.安装 BIND 软件包 2.配置主配置文件 3.创建缓存目录 4.启动并设置开机自启 一、主DNS服务器配置 1.安装 BIN…

使用 HTML + JavaScript 实现在线考试系统

在现代的在线教育平台中&#xff0c;在线考试系统是不可或缺的一部分。本文将通过一个完整的示例&#xff0c;演示如何使用 HTML、CSS 和 JavaScript 构建一个支持多种题型的在线考试系统。 效果演示 项目概述 本项目主要包含以下核心功能&#xff1a; 支持4种常见题型&…

谷歌工作自动化——仙盟大衍灵机——仙盟创梦IDE

下载地址 https://chromewebstore.google.com/detail/selenium-ide/mooikfkahbdckldjjndioackbalphokd https://chrome.zzzmh.cn/info/mooikfkahbdckldjjndioackbalphokd

秒杀系统—1.架构设计和方案简介

大纲 1.秒杀系统的方案设计要点 2.秒杀系统的数据 页面 接口的处理方案 3.秒杀系统的负载均衡方案底层相关 4.秒杀系统的限流机制和超卖问题处理 5.秒杀系统的异步下单和高可用方案 1.秒杀系统的方案设计要点 (1)秒杀促销活动的数据处理 (2)秒杀促销活动的页面处理 (…