源码 解析_最详细集合源码解析之ArrayList集合源码解析
从今天开始我会将集合源码分析陆陆续续整理,写成文章形成集合源码系列文章,方便大家学习
ArrayList集合源码其实相对比较简单,整个源码结构相对于HashMap等源码要好理解的多;先来看下ArrayList整体结构
ArrayList整体结构
1实现Cloneable接口
为什么要实现这个接口呢,ArrayList会调用clone()方法,这个方法是object的方法,调用该方法的对象必须要实现Cloneable接口,否则会抛出 CloneNoSupportException异常。
2实现 RandomAccess接口
这个接口就是一个标识接口,在JAVA官方定义中实现该接口的List就表示这个类可以实现快速随机访问,使用for循环获取数据的效率要优于迭代器获取数据得效率
3实现Serializable接口
这个接口也是个标识接口,代表可以被序列化。
4继承AbstractList并实现List接口
其实List接口定义了一些方法,实现该接口的类都必须实现这些方法,这些方法会在后续进行详细讲解,先放张图看看有哪些方法。
/** * 默认初始容量. */ private static final int DEFAULT_CAPACITY = 10;
/** * 空的数组实例 */ private static final Object[] EMPTY_ELEMENTDATA = {};
/** * 这也是空的数组实例,和EMPTY_ELEMENTDATA相比用于了解添加元素时数组膨胀多少 */ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/** * 存储ArrayList数据的数组 */ transient Object[] elementData; // non-private to simplify nested class access
/** * 表示集合的长度 * * @serial */ private int size;
1设置初始容量
public ArrayList(int initialCapacity) { //判断传的初始容量是否大于0 if (initialCapacity > 0) { //大于0就创建设置的容量大小的数组 this.elementData = new Object[initialCapacity]; } else if (initialCapacity == 0) { //等于0就创建空数组 this.elementData = EMPTY_ELEMENTDATA; } else { //小于0抛出异常 throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity); } }
2 无参构造
public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
3传入一个集合
public ArrayList(Collection extends E> c) { //将传入的集合转为数组赋值给elementDate elementData = c.toArray(); //将该数组的长度赋值给size并判断该集合是否为空 if ((size = elementData.length) != 0) { //判断数组元素对象是否为Object类 if (elementData.getClass() != Object[].class) //不是的话就重新复制 elementData = Arrays.copyOf(elementData, size, Object[].class); } else {
//传入的集合为空就创建一个空手数组 this.elementData = EMPTY_ELEMENTDATA; } }
这里面有两点需要注意一下,有的人看到这段代码可能会有疑惑,代码第一步就已经将集合转为数组传给elementData了,为什么下面还要进行一步判断然后进行数组复制呢?
这是因为toArray方法可能会存在返回的不是Object类型的数组,因此要多加一步判断进行校验;通过Arrays.copyOf方法进行重新赋值。
添加元素,添加元素算是ArrayList核心的方法,添加元素会涉及到数组的扩容操作。
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; }
首先会调用ensureCapaciity方法,将集合长度加一的值传入
第一步就是判断是不是要创建初始容量的数组,从这里可以看到字段属性的初始容量到现在才闪亮登场,可能一开始大家都觉得会是在构造函数进行设置初始容量大小的数组,这里要注意了,是在添加方法调用的。
这里要注意一点就是这个modCount,这个属性会贯穿整个ArrayList,只要涉及到数据的改变就会出现它。
如果需要的容量大于当前数组的长度,那就真正进入扩容的方法grow
首先判断将容量扩大1.5倍能否满足所需容量大小,如果不满足
就判断是否比最大数组长度大,如果还比它大就进行进一步的比较
private static int hugeCapacity(int minCapacity) { //如果小于0就抛出异常 if (minCapacity 0) // overflow throw new OutOfMemoryError(); //如果大于数组最大容量就扩容IntegerD的最大长度,小于的话就扩容为最大数组长度 return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
最后扩容的操作通过Arrays.copyof方法来执行
public E remove(int index) { rangeCheck(index);
modCount++; E oldValue = elementData(index);
int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index + 1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work
return oldValue; }
通过具体位置移除元素,首先就是先判断传入的位置是否越界;
然后同样的modCount++,标识在改变数据。
获取当前位置的元素值,用于返回
计算需要移动的数量
然后通过System.arraycopy完成数组的拷贝来移除元素,这个方法我在下面会单独介绍
将数组空出来的位置置空,方便垃圾回收器回收
public boolean remove(Object o) { if (o == null) { for (int index = 0; index index++) if (elementData[index] == null) { fastRemove(index); return true; } } else { for (int index = 0; index index++) if (o.equals(elementData[index])) { fastRemove(index); return true; } } return false; }
private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) System.arraycopy(elementData, index + 1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work }
传入具体值进行删除,大致分为两步,一步是for循环遍历找出与之匹配的元素索引位子,第二步就是根据索引位置进行删除。
从代码可知,先判断该元素是否为空,为空就遍历数组是否存在空的数据,找到后,调fastRemove方法传索引位置进行删除
如果不为空就for循环找到该元素,同样通过fastRemove方法进行删除
fastRemove方法和上一个remove方法大致一样,唯一区别就是去掉了索引位置校验,因为是先for获取的索引的位置,索引不会越界,减少不必要的代码
System.arraycopy方法
arraycopy(Object src, int srcPos, Object dest, int destPos, int length);
这个方法在arrayList的增删改都有用到,Array.copyof底层也是用的这个方法,这个方法主要有四个参数
Object src:源数组
int srcPos:源数组开始截取的起始位置
Object dest:目标数组
destPos 目标数组起始位置
int length:需要复制的长度
这四个参数具体什么意思呢,举个例子就明白了,比如:
Object[] arr={1,2,3,4,5,6}; Object[] newArr=new Object[5]; System.arraycopy(arr,1,newArr,0,4);
源数组arr,里面有6个元素,目标数组是个新数组,数组长度为5,
传参里的1就是从源数组第一个位置开始截取,放入目标数组,放入到目标数组第0个位置,截取长度为4,那么结果就是目标数组元素是2,3,4,5
源码 解析_最详细集合源码解析之ArrayList集合源码解析相关推荐
- 集合【7】--- 遍历ArrayList集合三种方法
集合相关知识总结: 集合[1] - 综述与ArrayList 集合[2] - LinkedList 集合[3] - ArrayList和LinkedList区别与联系(面试题) 集合[4]- Set ...
- java 准备 解析_深入理解JAVA虚拟机学习笔记24——类加载的准备和解析
每天进步一点点! 今天我们一起看一下类加载的准备阶段和解析阶段. 先看一下准备阶段:主要任务是在方法区中为类变量(仅static修饰变量,不包含实例变量)分配内存并设置类变量初始化的阶段. 这里面的区 ...
- python 字节码 汇编器_基础系列1-python解释器、PVM、源代码、字节码理解
大家都会听到这么一种说法,python是脚本语言,一门解释型语言,那么什么是解释型语言呢? 为了回答这个问题,我们从头撸起 语言又大致分为3类:机器语言.汇编语言.高级语言,简单的解释来说: 机器语言 ...
- java探针 字节码增强_深入浅出Java探针技术1--基于java agent的字节码增强案例
Java agent又叫做Java 探针,本文将从以下四个问题出发来深入浅出了解下Java agent 一.什么是java agent? Java agent是在JDK1.5引入的,是一种可以动态修改 ...
- mysql外码内码定义_刨根究底字符编码之六——简体汉字编码中区位码、国标码、内码、外码、字形码的区别及关系...
简体汉字编码中区位码.国标码.内码.外码.字形码的区别及关系 GB2312.GBK.GB18030等GB类汉字编码方案的具体实现方式是怎样的?区位码是什么?国标码是什么?内码.外码.字形码又是什么意思 ...
- Java基础知识强化之集合框架笔记27:ArrayList集合练习之去除ArrayList集合中的重复字符串元素...
1. 去除ArrayList集合中的重复字符串元素(字符串内容相同) 分析: (1)创建集合对象 (2)添加多个字符串元素(包含重复的) (3)创建新的集合 (4)遍历旧集合,获取得到每一个元素 (5 ...
- 杨老师课堂之ArrayList集合常用方法解析
ArrayList集合常用方法的解析 1.概述 在前面我们学习了数组,数组可以保存多个元素,但在某些情况下无法确定到底要保存多少个元素,此时数组将不再适用,因为数组的长度不可变.例如,要保存一个学 ...
- 华为荣耀5X解锁码申请及解锁详细教程
2019独角兽企业重金招聘Python工程师标准>>> 荣耀5X刷机包下载 华为荣耀5X怎么解锁?很多朋友购买了华为荣耀7都第一时间想获取ROOT权限,华为荣耀5X采用的是高通处理器 ...
- 【Groovy】集合声明与访问 ( 使用 [] 创建 ArrayList 和 LinkedList 集合 | 集合赋初值 | 使用下标访问集合 | 使用 IntRange 作为下标访问集合 )
文章目录 一.使用 [] 创建集合 1.使用 [] 创建 ArrayList 集合 2.使用 [] 创建 LinkedList 集合 二.访问集合中的元素 1.集合赋初值 2.使用下标访问集合元素 ( ...
最新文章
- html css发展前景,网页设计的发展趋势
- 计算机软件3十2二5,计算机软件基础3.2 操作系统.pdf
- django DateField需要前端传递的格式
- 失战于知识付费,会员与智能硬件将助蜻蜓FM打赢下半场战争?
- REVERSE-PRACTICE-CTFSHOW-4
- 手机号、姓名、邮箱等合法性验证方法
- response 流和写能一起吗_余甘果蜂蜜能一起吃吗?余甘果泡蜂蜜有什么功效?
- /dev/socket/vold exploit 本地提权漏洞
- Jeecg-Boot前后端分离版
- Mplayer 音量控制
- 2021软件测试技能大赛,2021软件测试国赛获奖感言
- IntelliJ Idea 下Png图片打开方式导致编码报错
- AI遇上农业会怎样?最新UNT《智慧农业》2022全面综述农业4.0发展的架构、技术、应用等
- 为什么包装类型间的相等判断应该用 equals
- 谷歌:民主国家搜索引擎的消费监控
- 电脑文档被删了怎么恢复?小方法好助手
- Linux服务器挂掉,使之自动重启脚本
- matlab期末数字图像处理小工具
- Overlapping Experiment Infrastructure: More, Better, Faster Experimentation
- MySQL 查询距离指定日期最近的数据
热门文章
- EDGE浏览器配合阿呆喵设置广告过滤
- cad vba编程从入门到精通_【CAD教程】CAD2020零基础入门到精通全套视频教程
- java restful项目打包_66-JT项目04(项目打包发布/JSON/项目业务)
- java 观察者模式_设计模式:全面通晓23种设计模式(典藏查阅)-第三部分
- Java方法02 递归
- 多目标粒子群算法_PSO粒子群算法可视化
- php上传图片并显示代码,php图片上传代码(完整版已测试)
- linux二进制数据16进制数据转换,[轉]16进制字符文本/二进制文件迷你互转器
- 小程序接入h5页面_微信小程序开发接入colorUI
- python的json.dump参数使用