Java ArrayList解密

        数组的大小是固定的,一旦创建的时候指定了大小,就不能再调整了。也就是说,如果数组满了,就不能再添加任何元素了。

        ArrayList 在数组的基础上实现了自动扩容,并且提供了比数组更丰富的预定义方法(各种增删改查),非常灵活。

        下面我们就来探究一下ArrayList的各个方法的实践和原理,以及算法复杂度

一、创建ArrayList

        创建方式:

ArrayList<String> alist = new ArrayList<String>();

        看看ArrayList的构造函数:

        如果初始化大小的话,会构造一个这样长度的object数组;如果初始化为0或者不填的话,就会创建一个空数组。

        也可以看出,如果非常确定 ArrayList 中元素的个数,在创建的时候还可以指定初始大小,可以有效地避免在添加新的元素时进行不必要的扩容。

二、添加元素 

alist.add("xxx");

        可以看出来,add过程分为两步,第一步确保数组的大小足够,第二步将元素添加到末尾

2.1 calculateCapacity

        进去看 ensureCapacityInternal,源码如下:

        那么首先先看 calculateCapacity,这个函数是为了确定数组的大小。

        如果数组是空的,那大小就是max {当前size+1,默认大小},源码中默认大小是10;如果数组不为空,那就返回他的指定大小

  

2.2 ensureCapacityInternal

        确保数组的大小足够。如果数组要溢出了,就要调用grow方法扩容 

 

2.3 grow

     // 检查是否会导致溢出,oldCapacity 为当前数组长度int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1); // 扩容至原来的1.5倍if (newCapacity - minCapacity < 0) // 如果还是小于指定容量的最小值newCapacity = minCapacity; // 直接扩容至指定容量的最小值if (newCapacity - MAX_ARRAY_SIZE > 0) // 如果超出了数组的最大长度newCapacity = hugeCapacity(minCapacity); // 扩容至数组的最大长度// 将当前数组复制到一个新数组中,长度为 newCapacityelementData = Arrays.copyOf(elementData, newCapacity);

2.4 总结

堆栈过程图示:
add(element)
└── if (size == elementData.length) // 判断是否需要扩容├── grow(minCapacity) // 扩容│   └── newCapacity = oldCapacity + (oldCapacity >> 1) // 计算新的数组容量│   └── Arrays.copyOf(elementData, newCapacity) // 创建新的数组├── elementData[size++] = element; // 添加新元素└── return true; // 添加成功

三,向指定位置添加元素

alist.add(0, "");

        上面讲清楚 ensureCapacityInternal之后,这里就很容易理解了。

public void add(int index, E element) {rangeCheckForAdd(index); // 检查索引是否越界ensureCapacityInternal(size + 1);  // 确保容量足够,如果需要扩容就扩容System.arraycopy(elementData, index, elementData, index + 1,size - index); // 将 index 及其后面的元素向后移动一位elementData[index] = element; // 将元素插入到指定位置size++; // 元素个数加一
}

3.1  rangeCheckForAdd

        判断索引是否越界,越界了就抛错 

3.2  ensureCapacityInternal

        上面已经讲过,不多赘述。

四、更新元素

alist.set(0, "");

 

public E set(int index, E element) {rangeCheck(index); // 检查索引是否越界E oldValue = elementData(index); // 获取原来在指定位置上的元素elementData[index] = element; // 将新元素替换到指定位置上return oldValue; // 返回原来在指定位置上的元素
}

4.1 elementData

        获取索引位置数组 

五、删除指定位置元素 

public E remove(int index) {rangeCheck(index); // 检查索引是否越界modCount++;    //集合实际被修改的次数+1,这个是ArrayList的一个成员变量E oldValue = elementData(index); // 获取要删除的元素int numMoved = size - index - 1; // 计算需要移动的元素个数if (numMoved > 0) // 如果需要移动元素,就用 System.arraycopy 方法实现System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // 将数组末尾的元素置为 null,让 GC 回收该元素占用的空间return oldValue; // 返回被删除的元素
}

六、删除指定元素 

list.remove("");

        分为元素为null和不为null的情况,区别在于,为null的时候用==判断,不为null的时候用equals判断,逻辑上并没有什么区别。

        如果找到元素,用fashRemove方法删除元素,返回true;找不到元素就返回false。 

 

6.1 fastRemove 

private void fastRemove(int index) {int numMoved = size - index - 1; // 计算需要移动的元素个数if (numMoved > 0) // 如果需要移动元素,就用 System.arraycopy 方法实现System.arraycopy(elementData, index+1, elementData, index,numMoved);elementData[--size] = null; // 将数组末尾的元素置为 null,让 GC 回收该元素占用的空间
}

七、查找元素 

list.indexOf("");
list.lastIndexOf("");

        如果要正序查找一个元素,可以使用 indexOf() 方法;如果要倒序查找一个元素,可以使用 lastIndexOf() 方法。

lastIndexOf() 方法和 indexOf() 方法类似,不过遍历的时候从最后开始。 

八、各个方法时间复杂度 

  • get        O(1)
  • add       最好O(1),在末尾插入;最坏O(n),插入后移动数组
  • remove 最好O(1),在末尾删除;最坏O(n),删除后移动数组
  • set        O(1)

 参考:

深入探讨 Java ArrayList:从源码分析到实践应用 | 二哥的Java进阶之路

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

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

相关文章

【Unity美术】Unity工程师对3D模型需要达到的了解【二】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

Git开发工具基本使用

文章目录 前言Git仓库基本概念基本环境安装清除原先配置生成秘钥配置Host添加公钥Github添加Gitee添加测试 本地仓库基本概览查看提交日志(log)版本回退添加文件至忽略列表分支分支冲突 远程仓库推送到远程仓库从远程仓库中抓取和拉取 在Idea中使用Git总结 前言 这里只是对Git…

从入门到精通UNet: 让你快速掌握图像分割算法

文章目录 一、UNet 算法简介1.1 什么是 UNet 算法1.2 UNet 的优缺点1.3 UNet 在图像分割领域的应用 二、准备工作2.1 Python 环境配置2.2 相关库的安装 三、数据处理3.1 数据的获取与预处理3.2 数据的可视化与分析 四、网络结构五、训练模型5.1 模型训练流程5.2 模型评估指标5.…

轻量封装WebGPU渲染系统示例<55>- 顶点数据更新

当前示例源码github地址: https://github.com/vilyLei/voxwebgpu/blob/feature/material/src/voxgpu/sample/VertexUpdateTest.ts 当前示例运行效果: ​​​​​​​ 此示例基于此渲染系统实现&#xff0c;当前示例TypeScript源码如下: export class VertexUpdateTest {pr…

App.vue中引入自定义组件

components目录中定义组件&#xff1a;Person.vue 目录截图&#xff1a; Person.vue文件中内容&#xff1a; <template><div class"person"><h2>姓名&#xff1a;{{name}}</h2><h2>年龄&#xff1a;{{age}}</h2><!--定义了…

数据的确权、流通、入表与监管研究(一):数据与确权(下)

关注WX公众号&#xff1a; commindtech77&#xff0c; 获得数据资产相关白皮书下载地址 1. 回复关键字&#xff1a;数据资源入表白皮书 下载 《2023数据资源入表白皮书》 2. 回复关键字&#xff1a;光大银行 下载 光大银行-《商业银行数据资产会计核算研究报告》 3. 回复关键字…

NX二次开发UFUN方式一键去除所有参数

一、概述 最近作了一个案例是通过遍历整个显示部件窗口的所有实体合并成一个部件&#xff0c;并且在导航器上也显示一个体&#xff0c;主要的思路是遍历当前所有实体&#xff0c;然后进行一键合并如图1所示&#xff0c;最后去除参数&#xff0c;这时导航器中显示一个体的记录。…

HelloWorld搭建(第一种模型)

1.创建Springboot项目并且引入依赖 <!-- 引入RabbitMQ的相关依赖 --> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>5.7.2</version> </dependency> 2.第一种模式(直连…

html-css-js移动端导航栏底部固定+i18n国际化全局

需求&#xff1a;要做一个移动端的仿照小程序的导航栏页面操作&#xff0c;但是这边加上了i18n国家化&#xff0c;由于页面切换的时候会导致国际化失效&#xff0c;所以写了这篇文章 1.效果 切换页面的时候中英文也会跟着改变&#xff0c;不会导致切换后回到默认的语言 2.实现…

动画墙纸:将视频、网页、游戏、模拟器变成windows墙纸——Lively Wallpaper

文章目录 前言下载github地址&#xff1a;网盘 关于VideoWebpagesYoutube和流媒体ShadersGIFs游戏和应用程序& more:Performance:多监视器支持&#xff1a;完结 前言 Lively Wallpaper是一款开源的视频壁纸桌面软件&#xff0c;类似 Wallpaper Engine&#xff0c;兼容 Wal…

功能真强大!5个令人惊叹的 Jupyter 黑科技

Jupyter 是一种功能强大的交互式计算环境&#xff0c;被广泛应用于数据分析、机器学习、科学计算等领域。 除了常见的基本功能外&#xff0c;Jupyter还隐藏着许多令人惊叹的黑科技&#xff0c;这些功能可以帮助用户更高效地完成工作&#xff0c;提升工作体验。 在本文中&…

linux线程与进程

简要 在Linux系统中&#xff0c;进程&#xff08;Process&#xff09;和线程&#xff08;Thread&#xff09;是操作系统中两个重要的概念&#xff0c;它们都是用于执行程序的执行单元&#xff0c;但有一些关键的区别。 在Linux系统中&#xff0c;可以使用fork系统调用创建新…