ArrayList详解

devtools/2024/9/20 7:04:31/ 标签: windows, spring boot, java-ee, spring, java

简介

【概述】

List的主要实现类,底层使用Object[]存储,适用于频繁的查找工作,线程不安全。

【特点】

  1. 增删慢:每次删除元素,都需要更改数组长度、拷贝以及移动元素位置;
  2. 查询快:由于数组在内存中是一块连续空间,因此可以根据地址+索引的方式快速获取对应位置上的元素。

【数据结构】

ArrayList底层是数组队列,相当于动态数组。

二、继承关系

【Serializable】

表示ArrayList支持序列化,能通过序列化去传输。

【Cloneable】

表示ArrayList重写了clone(),表示能被克隆。

【RandomAccess】

此接口的主要目的是允许通用算法更改其行为,以便在应用于随机访问列表或顺序访问列表时提供良好的性能。

//数据量特别大的时候,对返回的集合进行判断。
//如果返回的集合实现了RandomAccess就使用普通for,否则使用迭代器(增强for)
List<Student> list = ew ArrayList<>(); //~~~很多数据的集合
if(list instanceof RandomAccess){for (int i = 0; i < list.size(); i++) {System.out.println(list.get(i));}
}else {for (Student stu : list) {System.out.println(stu);}
}

三、源码分析

1、成员变量

// 默认的初始容量是10
private static final int DEFAULT_CAPACITY = 10;// 空元素数组
private static final Object[] EMPTY_ELEMENTDATA = {};// 默认容量的空数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};// 存储元素的数组 
transient Object[] elementData;// 集合最大容量2的31次方-1-8
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;// 集合容量
private int size;// 更改次数
protected transient int modCount = 0;

2、构造方法

// 1.构造一个初始容量的空数组。
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}// 2.构造具有指定初始容量的数组。
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);}
}
//3.构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序
public ArrayList(Collection<? extends E> c) {// 将集合构造中的集合对象转成数组Object[] a = c.toArray();if ((size = a.length) != 0) {if (c.getClass() == ArrayList.class) {// 类型为ArrayList则直接赋值elementData = a;} else {//如果不一样,使用Arrays的copyOf方法进行元素的拷贝elementData = Arrays.copyOf(a, size, Object[].class);}} else {// replace with empty array.elementData = EMPTY_ELEMENTDATA;}
}

3、成员方法

3.1 添加方法

3.1.1 指定的元素添加
public boolean add(E e) {// 对内部容量进行判断,详情见下方的被调方法ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;
}
3.1.2 指定位置、元素添加
public void add(int index, E element) {// 检查索引是否在范围内rangeCheckForAdd(index);// 对内部容量进行判断,详情见下方的被调方法ensureCapacityInternal(size + 1); // 实现从源数组的起始位置开始复制全部元素到目标数组的指定位置中System.arraycopy(elementData, index, elementData, index + 1,size - index);elementData[index] = element;size++;
}
3.1.3 指定集合中的所有元素添加
public boolean addAll(Collection<? extends E> c) {//源数组Object[] a = c.toArray();//源数组长度int numNew = a.length;// 对内部容量进行判断,详情见下方的被调方法ensureCapacityInternal(size + numNew);  // Increments modCount// 实现从源数组的起始位置开始复制全部元素到目标数组的指定位置中System.arraycopy(a, 0, elementData, size, numNew);size += numNew;return numNew != 0;
}
3.1.4 指定位置和集合中的所有元素添加
public boolean addAll(int index, Collection<? extends E> c) {// 检查索引是否在范围内rangeCheckForAdd(index);//源数组Object[] a = c.toArray();//源数组长度int numNew = a.length;// 对内部容量进行判断,详情见下方的被调方法ensureCapacityInternal(size + numNew);  // Increments modCountint numMoved = size - index;if (numMoved > 0)// 实现从源数组的起始位置开始复制全部元素到目标数组的指定位置中System.arraycopy(elementData, index, elementData, index + numNew,numMoved);// 实现从源数组的起始位置开始复制全部元素到目标数组的指定位置中System.arraycopy(a, 0, elementData, index, numNew);size += numNew;return numNew != 0;
}
3.1.5 扩容核心方法(重要)
// 1.主体函数
private void ensureCapacityInternal(int minCapacity) {// 对容量进行判断ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}// 2.通过最小容量和默认容量求出较大值 (用于第一次扩容)
private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;
}// 3.判断是否需要进行扩容
private void ensureExplicitCapacity(int minCapacity) {// 实际修改集合次数+1 (在扩容的过程中没用,主要是用于迭代器中)modCount++;// 判断当前最小容量是否大于数组长度if (minCapacity - elementData.length > 0)// 将计算出来的容量传递给核心扩容方法grow(minCapacity);
}// 4.核心扩容方法
private void grow(int minCapacity) {// 记录数组的实际长度int oldCapacity = elementData.length;// 核心扩容算法,原容量的1.5倍int newCapacity = oldCapacity + (oldCapacity >> 1);// 判断新容量是否小于当前最小容量(第一次调用add方法必然小于)if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 判断新容量是否大于最大数组长度,如果if (newCapacity - MAX_ARRAY_SIZE > 0)// 条件满足就计算出一个超大容量newCapacity = hugeCapacity(minCapacity);// 调用数组工具类方法,创建一个新数组,将新数组的地址赋值给elementDataelementData = Arrays.copyOf(elementData, newCapacity);
}// 5.超大容量计算
private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;
}
3.1.6 添加方法备注

ensureCapacity方法】

ArrayList添加大量元素之前通过该方法预设数组大小,以减少增量重新分配的次数。

// 确保集合至少可以容纳参数指定的元素数。
public void ensureCapacity(int minCapacity) {// 最小扩容int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)? 0: DEFAULT_CAPACITY;if (minCapacity > minExpand) {ensureExplicitCapacity(minCapacity);}
}

3.2 删除方法

3.2.1 根据索引删除
public E remove(int index) {// 检查索引是否在范围内rangeCheck(index);// 实际修改集合次数+1 (在扩容的过程中没用,主要是用于迭代器中)modCount++;// 获取当前索引原来的数据E oldValue = elementData(index);// 计算集合需要移动元素的个数int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);、// 将源集合最后一个元素置为null,尽早让垃圾回收机制对其进行回收elementData[--size] = null; //返回当前索引原来的数据return oldValue;
}
3.2.2 根据对象删除
// 1.根据对象删除
public boolean remove(Object o) {if (o == null) {for (int index = 0; index < size; index++)// 遍历并判断集合的元素是否为nullif (elementData[index] == null) {// null则用fastRemove方法快速删除fastRemove(index);return true;}} else {for (int index = 0; index < size; index++)// 用o对象的equals方法和集合每一个元素进行比较if (o.equals(elementData[index])) {// 如果相等,调用fastRemove方法快速删除fastRemove(index);return true;}}// 如果集合没有o该元素,那么就会返回falsereturn false;
}// 2. 
private void fastRemove(int index) {// 实际修改集合次数+1 (在扩容的过程中没用,主要是用于迭代器中)modCount++;// 计算集合需要移动元素的个数int numMoved = size - index - 1;if (numMoved > 0)System.arraycopy(elementData, index+1, elementData, index,numMoved);// 将源集合最后一个元素置为null,尽早让垃圾回收机制对其进行回收elementData[--size] = null; // clear to let GC do its work
}

3.3 获取方法

public E get(int index) {// 检查索引是否在范围内rangeCheck(index);// 获取该索引元素return elementData(index);
}

3.4 修改方法

public E set(int index, E element) {// 检查索引是否在范围内rangeCheck(index);// 获取当前索引原来的数据E oldValue = elementData(index);// 替换后返回旧数据elementData[index] = element;return oldValue;
}

四、问题总结

1、ArrayList的扩容机制怎么描述?

  1. 添加元素的时候会判断是不是空参构造时创建的空数组;
  2. 是则将最小容量设为 10,否则为 size + 1;
  3. 判断当前最小容量是否大于数组长度;
  4. 大于则进行扩容,扩容为当前的数组长度的 1.5 倍;
  5. 判断扩容后容量是否小于当前最小容量(第一次添加必然是);
  6. 是则将最小容量赋值给扩容后容量,否则继续;
  7. 判断新容量是否大于最大数组长度;
  8. 是则判断当前最小容量是否大于最大数组长度;
  9. 是则新容量为 Integer 最大值,否则为最大数组长度;


http://www.ppmy.cn/devtools/97219.html

相关文章

SSM学生社团管理系统—计算机毕业设计源码20360

目 录 摘要 1 绪论 1.1 研究背景 1.2 研究意义 1.3论文结构与章节安排 2 学生社团管理系统系统分析 2.1 可行性分析 2.2 系统流程分析 2.2.1 数据增加流程 2.2.2 数据修改流程 2.2.3 数据删除流程 2.3 系统功能分析 2.3.1 功能性分析 2.3.2 非功能性分析 2.4 系…

YouTube最好用的翻译插件

相信很多同学和我一样&#xff0c;想看YouTube视频时发现基本上都说英文&#xff0c;以我的英文水平&#x1f923;去观看真是一言难尽&#xff0c;所以就想着看能不能在谷歌浏览器上找一个插件来进行翻译&#xff0c;结果还真让我找到了一个不错的Youtube翻译插件&#xff0c;它…

AI大模型高效题库生成:业务人效提升的强大助力

一 现状问题 1、培训考核涉及的文件数量较多 当前&#xff0c;京东航空公司维修部门面临着人员规模的快速增长和持续的培训需求。根据民航局的规定&#xff0c;维修培训必须确保所有维修人员都能够完成对飞机维修相关文件的学习&#xff0c;这包括维修方案、维修工程管理手册…

Anaconda环境迁移之conda pack

目录 1. conda pack安装2. 环境打包3. 拷贝环境包到目标电脑4. 激活环境5. 大功告成 1. conda pack安装 源电脑安装conda pack conda install conda-pack2. 环境打包 假如环境名为test&#xff0c;那么打包命令如下&#xff1a; conda pack -n test -o test.tar.gz打包后的…

使用Python+MoviePy给视频添加字幕或水印

一、使用CompositeVideoClip将使用TextClip创建文字类与视频叠加在一起&#xff0c;给视频添加字幕或水印 from moviepy.editor import *# 从本地载入视频myHolidays.mp4&#xff0c;并截取00:00:50 - 00:00:60部分 clip VideoFileClip("/home/Download/Mojito.mp4"…

如果忘记了 Apple ID 密码,如何重设

“我忘记了我的 Apple ID 密码&#xff0c;如何恢复我的帐户&#xff1f;”为了方便用户&#xff0c;Apple 允许每个人使用唯一的 Apple ID 和密码激活设备并访问所有 Apple 服务。然而&#xff0c;实际上&#xff0c;手动选择某项并忘记它似乎很容易。例如&#xff0c;许多 Ap…

带你速通C语言——指针(10)

指针是C语言中最强大但也最容易引起困惑的概念之一。它们直接关联内存管理&#xff0c;使得程序员可以高效地操作数据和内存。下面我将尽量以简单明了的方式介绍指针的基本概念。 1.指针基础 指针本质上是存储内存地址的变量&#xff0c;这个地址指向一个值。通过指针&#xf…

Vue中调整组件的高度及其他样式

在Vue组件中&#xff0c;如果想让一个组件如<MapContainer />变长&#xff0c;你可以使用CSS来调整它的高度。以下是一些可能的方法&#xff1a; 设置固定高度&#xff1a;直接给<MapContainer />组件设置一个高度值。 .MapContainer {height: 300px; /* 你可以根据…

论文中绘制神经网络工具汇总

论文中绘制神经网络工具汇总_convnetdraw-CSDN博客 这哥们总结的还是比较全面的&#xff01;

MongoDB如何实现大于小于查询

MongoDB是一个高性能、开源、无模式的文档型数据库&#xff0c;它使用BSON&#xff08;Binary JSON&#xff09;作为存储格式&#xff0c;支持丰富的查询语法&#xff0c;包括大于&#xff08; g t &#xff09;、小于&#xff08; gt&#xff09;、小于&#xff08; gt&#x…

RabbitMQ-消息队列延迟队列二

1、安装rabbitmq 怎么安装rabbitmq请查看之前课程&#xff0c;如果已经安装&#xff0c;请略过此步。 2、创建vendor文件夹或是直接采用PHP框架 mkdir vendor 3、进入文件 cd vendor 4、安装php扩展 composer require php-amqplib/php-amqplib 5、进入上级创建delay文件…

豆瓣评分8.6!Python社区出版的Python故事教程,太强了!

Python 是活力四射的语言&#xff0c;是不断发展中的语言。就连使用 Python 多年的行者也不敢说对 Python 的方方面面都了解并可以自由运用&#xff0c;想必读者可能更加无法快速掌握所有重点技巧了。 今天给小伙伴们分享的这份手册是用互动的开发故事来探讨Pyfhonic开发的故事…

Spring之@Bean注解

1. 使用方式 1.1 Configuration Bean 1.1.1 创建实体类 User Data NoArgsConstructor public class User {private String name;public User(String name) {this.name name;} } 1.1.2 创建配置类 UserConfig Configuration public class UserConfig {Beanpublic User us…

图片加载的区域显示

问题描述: 一个imageview 控件宽高 640*280 ,通过glide加载要显示的图片,,图片始终显示在imageview 的右边和下边,图片的宽高可能小于或者大于imageview 控件的宽高,如何在imageview 显示图片:如果图片的宽高大于imageview 的宽高可以只显示图片对应的右下部分(此时imageview …

C++:list类(迭代器)

目录 前言 数据结构 push_back push_front 默认构造函数 拷贝构造函数 list迭代器 结构 构造函数 *运算符重载 ->运算符重载 前置运算符重载 后置运算符重载 前置--运算符重载 后置--运算符重载 !运算符重载 运算符重载 list迭代器完整代码 begin和end …

【Leetcode 1189 】 “气球” 的最大数量 —— 数组模拟哈希表

给你一个字符串 text&#xff0c;你需要使用 text 中的字母来拼凑尽可能多的单词 "balloon"&#xff08;气球&#xff09;。 字符串 text 中的每个字母最多只能被使用一次。请你返回最多可以拼凑出多少个单词 "balloon"。 示例 1&#xff1a; 输入&#…

mysql:表的约束(空属性,默认值,comment,zerofill,主键,唯一键,外键)

目录 表的约束 空属性 默认值(defualut) comment:列描述 zerofill &#xff1a;显示约束 主键 自增长&#xff1a;auto_increment 唯一键 外键 查询数据 表的约束 真正约束字段的是数据类型&#xff0c;但是数据类型约束很单一&#xff0c;需要有一些额外的约束&…

第22天笔记

C语言编译步骤 预处理 编译 汇编 链接 什么是预处理 预处理就是在源文件&#xff08;如.c文件&#xff09;编译之前&#xff0c;所进行的一部分预备操作&#xff0c;这部分操作是由预处理 程序自动来完成&#xff1b;当源文件在编译时&#xff0c;编译器会自动调用预处理程…

[游戏开发] LuaTable转string存读二进制文件

UE5和Unity通用此方案&#xff0c;只不过读写文件的接口略有不同&#xff0c;lua代码的处理是相同的。 下面两个方法是 LuaTable和字符串互相转换的代码 function XUtils.luaTableToString(tab, sp)sp sp or ""local s ""for k,v in pairs(tab) doif t…

【网络安全】P2:访问控制失效

未经许可&#xff0c;不得转载。 文章目录 正文 正文 该漏洞允许我绕过电子邮件验证步骤&#xff0c;无需我点击发送到我的收件箱的链接。 某应用程序提示我&#xff0c;只有验证我的帐户才能使用网站上的一项功能。当我点击“开始验证”并拦截请求时&#xff0c;请求体如下&am…