千家信息网

java集合中ArrayList源码解析是怎样的

发表于:2024-12-12 作者:千家信息网编辑
千家信息网最后更新 2024年12月12日,今天就跟大家聊聊有关java集合中ArrayList源码解析是怎样的,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。整体架构ArrayList实
千家信息网最后更新 2024年12月12日java集合中ArrayList源码解析是怎样的

今天就跟大家聊聊有关java集合中ArrayList源码解析是怎样的,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

整体架构

ArrayList实际就是个数组结构,如图

index:数组下标
elementData:数组本身

其他基本概念:

/** * Default initial capacity. * 数组初始大小 */ private static final int DEFAULT_CAPACITY = 10;
/** * The size of the ArrayList (the number of elements it contains). * 当前数组大小 * @serial */private int size;
//表示当前数组被修改的版本次数protected transient int modCount = 0;

类注释:
1、可以自动扩容
2、允许put null
3、size、set、put、add、get时间复杂度O(1)
4、非线程安全(作为共享变量时存在),必要时可以使用线程安全的SynchronizedList(性能低)

public boolean add(E e) {    synchronized (mutex) {// synchronized 是一种轻量锁,mutex 表示一个当前 SynchronizedList        return c.add(e);    }}

5、增强for循环或迭代时,若数组大小改变,则抛出异常

源码解析

一、初始化:

//1、指定大小初始化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);    }}//2、无参初始化 数组大小为空//初始化时数组大小不是默认的10,第一次add时扩容为10public ArrayList() {    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}//3、指定初始数据初始化public ArrayList(Collection 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;    }}

二、新增、扩容、删除

新增:

public boolean add(E e) {  //确保数组大小是否足够,不够执行扩容,size 为当前数组的大小  ensureCapacityInternal(size + 1);  // Increments modCount!!  //直接赋值,线程不安全的  elementData[size++] = e; 对null无校验,因此可以存入null值  return true;}

扩容:

private void ensureCapacityInternal(int minCapacity) {  //如果初始化数组大小时,有给定初始值,以给定的大小为准,不走 if 逻辑  if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {    minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);  }  //确保容积足够  ensureExplicitCapacity(minCapacity);}private void ensureExplicitCapacity(int minCapacity) {  //记录数组被修改  modCount++;  // 如果我们期望的最小容量大于目前数组的长度,那么就扩容  if (minCapacity - elementData.length > 0)    grow(minCapacity);}//扩容,并把现有数据拷贝到新的数组里面去private void grow(int minCapacity) {  int oldCapacity = elementData.length;  // oldCapacity >> 1 是把 oldCapacity 除以 2 的意思  这里注意:ArrayList扩容是1+0.5倍  int newCapacity = oldCapacity + (oldCapacity >> 1);  // 如果扩容后的值 < 我们的期望值,扩容后的值就等于我们的期望值   if (newCapacity - minCapacity < 0)    newCapacity = minCapacity;  // 如果扩容后的值 > jvm 所能分配的数组的最大值,那么就用 Integer 的最大值  if (newCapacity - MAX_ARRAY_SIZE > 0)    newCapacity = hugeCapacity(minCapacity);  扩容时源码中对上下数组大小做了校验,有很清晰的数组大小溢出意识,平时开发时应借鉴  // 通过复制进行扩容  elementData = Arrays.copyOf(elementData, newCapacity);}

扩容动态图:

删除:

public boolean remove(Object o) {  // 如果要删除的值是 null,找到第一个值是 null 的删除  if (o == null) {    for (int index = 0; index < size; index++)      if (elementData[index] == null) { 可以删除null值        fastRemove(index);        return true;      }  } else {    // 如果要删除的值不为 null,找到第一个和要删除的值相等的删除    for (int index = 0; index < size; index++)      // 这里是根据  equals 来判断值相等的,相等后再根据索引位置进行删除      if (o.equals(elementData[index])) {  //找到需要删除的索引        fastRemove(index);        return true;      }  }  return false;}//根据索引位置进行元素删除private void fastRemove(int index) {  // 记录数组的结构要发生变动了  modCount++;  // numMoved 表示删除 index 位置的元素后,需要从 index 后移动多少个元素到前面去  // 减 1 的原因,是因为 size 从 1 开始算起,index 从 0开始算起  int numMoved = size - index - 1;  if (numMoved > 0)    // 从 index +1 位置开始被拷贝,拷贝的起始位置是 index,长度是 numMoved    System.arraycopy(elementData, index+1, elementData, index, numMoved);  //数组最后一个位置赋值 null,帮助 GC  elementData[--size] = null;}

三、迭代器

implement java.util.Iterator

迭代器的一些参数:
int cursor;// 迭代过程中,下一个元素的位置,默认从 0 开始。
int lastRet = -1; // 新增场景:表示上一次迭代过程中,索引的位置;删除场景:为 -1。
int expectedModCount = modCount;// expectedModCount 表示迭代过程中,期望的版本号;modCount 表示数组实际的版本号。

迭代器源码分析:

//是否存在下一个可迭代的值public boolean hasNext() {  return cursor != size;//cursor 表示下一个元素的位置,size 表示实际大小,如果两者相等,说明已经没有元素可以迭代了,如果不等,说明还可以迭代}//下一个可迭代的值public E next() {  //迭代过程中,判断版本号有无被修改,有被修改,抛 ConcurrentModificationException 异常  checkForComodification();  //本次迭代过程中,元素的索引位置  int i = cursor;  if (i >= size)    throw new NoSuchElementException();  Object[] elementData = ArrayList.this.elementData;  if (i >= elementData.length)    throw new ConcurrentModificationException();  // 下一次迭代时,元素的位置,为下一次迭代做准备  cursor = i + 1;  // 返回元素值  return (E) elementData[lastRet = i];}// 版本号比较final void checkForComodification() {  if (modCount != expectedModCount)    throw new ConcurrentModificationException();}public void remove() {  // 如果上一次操作时,数组的位置已经小于 0 了,说明数组已经被删除完了  if (lastRet < 0)    throw new IllegalStateException();  //迭代过程中,判断版本号有无被修改,有被修改,抛 ConcurrentModificationException 异常  checkForComodification();  try {    ArrayList.this.remove(lastRet);    cursor = lastRet;    // -1 表示元素已经被删除,这里也防止重复删除lastRet = -1;    // 删除元素时 modCount 的值已经发生变化,在此赋值给 expectedModCount    // 这样下次迭代时,两者的值是一致的了    expectedModCount = modCount;  } catch (IndexOutOfBoundsException ex) {    throw new ConcurrentModificationException();  }}

看完上述内容,你们对java集合中ArrayList源码解析是怎样的有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。

数组 迭代 大小 位置 元素 版本 过程 源码 期望 安全 内容 实际 拷贝 线程 最大 场景 数据 最大值 期望值 索引 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 软件开发p端和c端 呼和浩特软件开发测试 ajax操作数据库技术 触点互联网科技有限公司文化 上汽软件开发部 IT储备生 大企业网站用自己服务器吗 导入数据库文件的时候电脑卡住了 厅网络安全和信息化领导小组 电脑服务器名怎么看 线上数据库出现tx锁 汉阳服务器内存回收怎么样 徐州安卓系统软件开发公司 腾讯云服务器无法通过密码登录 服务器监控如何搭建图 我的世界服务器创造权限怎么给 计算机网络安全体制 医疗软件开发怎么样 国内外网络安全形势严峻 2001年国家明确提出增强网络安全 轻量化软件开发 分析炒股的软件开发 义乌软件开发一人天多少钱 杭州制造执行系统软件开发 深圳艾灵网络技术有限公司 内蒙古学生网络技术服务计划表 网站香港服务器带宽多少合适 微博服务器异常时内容照常浏览 微信 数据库 读取数据 金融业网络安全重要意义 财政网络安全维护
0