ArrayList源码分析

告别兼容性困扰:在F-Droid中安全部署Termux的完整指南 本文提供了一份在F-Droid中安全部署Termux的完整指南,旨在彻底解决Android设备上的兼容性困扰。文章详细阐述了为何选择F-Droid而非Google Play商店,并逐步指导用户完成从彻底清理旧版本、安装F-Droid客户端、授予权限到最终安装配置Termux的全过程。指南还涵盖了进阶的稳定性配置、数据迁移方法以及常见问题排查,确保用户获得一个稳定、无冲突的命令行工作环境。 阅读详情

数据结构

底层的数据结构就是数组,数组元素类型为Object类型

源码分析

一、类的继承关系

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable

说明:ArrayList继承AbstractList抽象父类,实现了List接口(规定了List的操作规范)、RandomAccess(可随机访问)、Cloneable(可拷贝)、Serializable(可序列化)。
接口讲解

二、成员变量

private static final int DEFAULT_CAPACITY = 10;
transient Object[] elementData; 
private int size;

private static final Object[] EMPTY_ELEMENTDATA = {};
/**用于默认大小的空实例的共享空数组实例。我们将其与空元素数据区分开,以知道何时充气添加第一个元素*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

成员变量 transient关键字

节省空间
因为elementData里面不是所有的元素都有数据,因为容量的问题,elementData里面有一些元素是空的,这种是没有必要序列化的。ArrayList的序列化和反序列化依赖writeObject和readObject方法来实现。可以避免序列化空的元素。

成员变量 2个空数组意义

//list1.elementData为EMPTY_ELEMENTDATA,
ArrayList list1 = new ArrayList(0);
//list2.elementData为DEFAULTCAPACITY_EMPTY_ELEMENTDATA
ArrayList list2 = new ArrayList();

添加首个元素时,扩容区别
list1 扩容为 1
list2 扩容为 DEFAULT_CAPACITY = 10

线程安全

只有当 ArrayList 作为共享变量时,才会有线程安全问题,当 ArrayList 是方法内部局部变量时,是没有线程安全问题的。

本质: ArrayList 自身的 elementData、size、modCount 在进行各种操作时,都没有加锁,而且这些变量的类型并非是可见的(volatile)的,所以如果多个线程对这些变量进行操作时,可能会有值被覆盖的情况。

三、构造函数

public ArrayList(int initialCapacity) {
        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);
        }
    }
    public ArrayList() { 
        // 无参构造函数,设置元素数组为空 
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
   
    public ArrayList(Collection<? extends E> c) { // 集合参数构造函数
        elementData = c.toArray(); // 转化为数组
        if ((size = elementData.length) != 0) { // 参数为非空集合
            if (elementData.getClass() != Object[].class) // 是否成功转化为Object类型数组
                elementData = Arrays.copyOf(elementData, size, Object[].class); // 不为Object数组的话就进行复制
        } else { // 集合大小为空,则设置元素数组为空
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

四、新增和扩容

新增主要分为两步:

  1. 判断是否需要扩容,如果需要则执行扩容操作;
  2. 赋值新元素。

add函数

public boolean add(E e) {
	// 确保数组大小是否足够,不够则执行扩容,size 为当前数组的大小
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 直接赋值
    elementData[size++] = e;
    return true;
}

扩容步骤(ensureCapacityInternal)源码:

private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

// 计算 capacity
private static int calculateCapacity(Object[] elementData, int minCapacity) {
	// 无参构造 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    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 void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    // 新的容量为旧的容量的 1.5 倍(capacity >> 1  ==> capacity / 2)
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 如果新的容量小于我们期望值 minCapacity,则将新的容量值赋值为期望值
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    // 如果新的容量大于 JVM 所能分配的数组的最大值,那么就用 Integer 的最大值
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 通过复制进行扩容
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

注意点

  1. 扩容的规则并不是翻倍,而是 原来容量大小 + 原来容量大小的一半,直白来说,扩容后的大小是原来容量的 1.5 倍;
  2. ArrayList 中数组的最大容量是 Integer.MAX_VALUE,超过这个值,JVM 就不会给数组分配内存空间了;
  3. 新增时,并没有对值进行严格的校验,所以 ArrayList 是允许 null 值的。

扩容的本质
Arrays.copyOf(elementData, newCapacity)

public static int[] copyOf(int[] original, int newLength) {
    int[] copy = new int[newLength];
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);

五、删除和缩容

remove函数

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);
        // 赋值为空,有利于进行GC
        elementData[--size] = null; 
        // 返回旧值
        return oldValue;
    }

缩容的本质

public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length);

数组复制几种方法效率链接

fail-fast 机制

fail-fast 机制是java集合(Collection)中的一种错误机制。它只能被用来检测错误,因为JDK并不保证fail-fast机制一定会发生。
一种理念,在进行系统设计时优先考虑异常情况

什么时候会出现fail-fast?

以下两种情况下会导致fail-fast,抛出ConcurrentModificationException

  • 单线程环境
    遍历一个集合过程中,集合结构被修改。注意,listIterator.remove()方法修改集合结构不会抛出这个异常。
  • 多线程环境
    当一个线程遍历集合过程中,而另一个线程对集合结构进行了修改。

原理

// AbstractList中唯一的属性
// 用来记录List修改的次数:每修改一次(添加/删除等操作),将modCount+1
protected transient int modCount = 0;

// ArrayList 代码
final void checkForComodification() {
    if (expectedModCount != ArrayList.this.modCount)
          throw new ConcurrentModificationException();
    }

执行 checkForComodification()。若 “modCount 不等于 expectedModCount”,则抛出ConcurrentModificationException异常,产生fail-fast事件。

fail-fast 解决方式

使用java.util.concurrent包下的类去取代java.util包下的类
例如:private static List list = new ArrayList();
替换为:private static List list = new CopyOnWriteArrayList();

华为云CodeHub保姆级教程:从零开始,5分钟搞定本地代码上传(含常见报错解决) 本文提供华为云CodeHub的保姆级教程,从零开始5分钟完成本地代码上传,涵盖环境准备、仓库创建、远程连接及常见报错解决。特别适合新手开发者快速掌握Git命令与云端协作流程,确保代码安全高效上传。 阅读详情

相关推荐

InSAR数据获取实战指南:Sentinel-1、精密轨道与ALOS PALSAR DEM的下载与应用

本文详细介绍了InSAR数据获取的实战指南,重点解析Sentinel-1数据、精密轨道数据(OPOD)和ALOS PALSAR DEM的下载与应用技巧。通过实际案例和代码示例,帮助读者掌握数据筛选、下载加速、校验及多源数据协同使用的关键方法,提升地面形变监测的精度与效率。

weixin_29215815的博客 433

【JDK源码分析系列】ArrayList源码分析

【JDK源码分析系列】ArrayList源码分析 【1】ArrayList源码MCL视图 ArrayList的MCL视图如下图所示,包括内部类的MCL视图; 【2】ArrayList源码分析相关知识点总结 /** * 如果用transient声明一个实例变量,当对象存储时,它的值不需要维持; * 即用transient关键字标记的成员变量不参与序列化过程;...

CopperSun 技术博客 610

电赛经验分享——赛前准备

⏩ 大家好哇!我是小光,想要成为系统架构师的嵌入式爱好者。⏩在之前的电赛中取得了省一的成绩,本文对电赛比赛前需要准备什么做一个经验分享。加入小光嵌入式交流群(qq群号:737327353)免费获取博主所有资料哦!

qq_52608074的博客 7779

Java基础之ArrayList源码分析

ArrayList是Java开发中使用比较频繁的一个类,通过对源码的解读,可以了解ArrayList的内部结构以及实现方法,清楚它的优缺点,以便我们在编程时灵活运用。

Soinice的博客 403

ROS专栏—基于STM32F103的INA219电源数据采集

文章主要记录了stm32f103对ina219的数据采集

热爱生活、奋斗前行 2354

操作系统知识点汇总(王道)

操作系统不论是就业还是升学都是核心的一门课程,对于大部分工科更是必修课程。本文章是对《王道操作系统》所有知识点的总结笔记,以供大家学习以及在备考期间复习使用。让我们一起把知识往脑子里旋好吗!!!......

S mile0804的博客 5万+

ArrayList 源码分析

例如,在 Java 中,如果一个对象包含一个基本数据类型的成员变量和一个引用类型的成员变量,进行浅拷贝时,基本数据类型的成员变量会被复制一份,而引用类型的成员变量只是复制了引用,两个对象仍然指向同一个引用对象。浅拷贝是创建一个新的对象,但是这个新对象的成员变量仍然指向原始对象的成员变量所指向的内存地址。也就是说,浅拷贝只是复制了对象的引用,而不是对象的实际内容。在其他编程语言中,如 Python、C++等,也有类似的深拷贝和浅拷贝的概念和实现方式,但具体的语法和方法会有所不同。方法进行实际的容量调整检查。

Wzideng@qq.com 982

hwasan实例分析踩内存之一:camx进程stack tag-mismatch的三种解决方案

hwasan实例分析踩内存之一:camx进程stack tag-mismatch的三种解决方案。

1291

131面试必问:ArrayList源码分析&手写ArrayList

简介 ArrayList是我们开发中非常常用的数据存储容器之一,其底层是数组实现的,我们可以在集合中存储任意类型的数据,ArrayList是线程不安全的,非常适合用于对元素进行查找,效率非常高。 线程安全性 对ArrayList的操作一般分为两个步骤,改变位置(size)和操作元素(e)。所以这个过程在多线程的环境下是不能保证具有原子性的,因此ArrayList在多线程的环境下是线程不安全的。 更多面试题:面试题内容聚合 源码分析 1. 属性分析 /** *默认初始化容量 ...

AI乔治 211

Spring AI提示词模板应用

本文介绍了使用SpringAI提示词模板的配置方法,主要步骤包括:1)在JDK17和SpringBoot3.4.4环境下引入spring-ai-alibaba1.0.0.2依赖;2)配置application.yml文件;3)创建测试控制器类处理提示词模板(.st文件);4)通过模板变量动态生成AI回复内容。演示了如何通过@RequestParam设置默认参数,实现无需前端传参即可测试AI回复功能,最终完成了一个SpringAI提示词模板的应用Demo。

wjianwei666的专栏 198

ResNet改进(7):添加 SK注意力机制

实现 SK 注意力机制。

2401_82355416的博客 732

ArrayList的源码分析

来源:https://www.cnblogs.com/beppezhang/p/6560710.html 在项目中经常会用到list集合来存储数据,而其中ArrayList是用的最多的的一个集合,这篇博文主要简单介绍ArrayList的源码分析,基于JDK1.7: 这里主要介绍 集合 的属性,构造器,和方法:方法主要基于 add(E e),get(int index),remove(int i...

rentian1的博客 228

基于STM32、HAL库的WF162A 11BA T&R气压传感器 驱动程序设计

/ 转换为7位地址。* @param temperature: 存储读取到的温度值(°C)* @param temperature: 存储读取到的温度值(°C)* @param pressure: 存储读取到的压力值(bar)* @param pressure: 存储读取到的压力值(bar)* @param range: 压力范围(bar)

corlin6688的博客 363

ArrayList常用方法及源码分析

ArrayList常用方法!!!!!!!!!!!

o676448的博客 418

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

Vector,ArrayList,Linkedlist源码分析

list接口和set集合

Cocobuhui的博客 973

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Java源码分析之ArrayList

ArrayList源码分析 ArrayList作为一个java中对数据进行存储的容器,是java中List<E>接口的核心实现类 有句古话这么来着:“万变不离其宗”,观其源码,通俗的来说,ArrayList其实就是一个数组 数组众所周知,他的定义必须申请一个规定长度的存储空间,是顺序存储结构,但是ArrayList 似乎可以无限制的添加数据,这是为何,这其实是这种扩容机制系统悄悄地在背后棒你做了 就形成了表面上可以无限制地添加数据的现象 下面,让我们来看看ArrayList的源码分析.

qq_48201696的博客 441

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

ArrayList和LinkedList的区别和源码分析

从源码可以看出,ArrayList想要get(int index)元素时,直接返回index位置上的元素,而LinkedList需要通过for循环进行查找,虽然LinkedList已经在查找方法上做了优化,比如index < size / 2,则从左边开始查找,反之从右边开始查找,但是还是比ArrayList要慢。ArrayList自由性较低,因为它需要手动的设置固定大小的容量,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用;//向左挪一位,index位置原来的数据已经被覆盖了。

weixin_62198635的博客 732

java ArrayList源码分析

ArrayList是 Java 标准库中的一个非常常用的类,它实现了List接口,提供了一个动态数组的实现,可以用来存储一系列的元素。sizeArrayListArrayListArrayListsizeadd(E e)clear()size()ArrayList还实现了一些其他的内部方法和嵌套类,比如Itr(迭代器)、ListItr(列表迭代器)、SubList(子列表)等,这些类和方法支持ArrayList的高级操作,如迭代、元素搜索、子列表操作等。此外,ArrayList还重写了clone()

qq_36070104的博客 1093

ArrayList中add方法源码分析~

首先要明确ArrayList的底层实现是数组利用空参创建的集合时,在底层创建一个默认长度为0的数组添加第一个元素时,底层会创建一个新的长度为10的数组存满时,会扩容1.5倍如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准。

2501_90392153的博客 1904

源码分析_JDK8 ArrayList源码分析

ArrayList类结构图实现Serializable接口的意义:如果可序列化类未显式声明serialversionUID,则序列化运行时将计算默认的serialVersionUID值。默认的serialversionUID计算是对类细节高度敏感,这些细节可能因编译器而异,因此可能在反序列化过程中可能导致意外的InvalidClassException。因此,为了在不同Java编译器之间保证一致的...

weixin_39841717的博客 70

java集合之ArrayList源码分析

ArrayList是一种以数组实现的List,与数组相比,它具有动态扩展的能力,因此也可称之为动态数组。

2501_92608043的博客 805

【JDK源码】ArrayList源码分析

目录 源码分析 1、构造方法 2、扩容机制 3、遍历删除时的陷阱 4、Java8新增方法 5、其他方法 源码分析 1、构造方法 2、扩容机制 3、遍历删除时的陷阱 4、Java8新增方法 5、其他方法 ...

小确幸的博客 387

面试必会之ArrayList源码分析&手写ArrayList

注:本文所有方法和示例基于jdk1.8 简介 ArrayList是我们开发中非常常用的数据存储容器之一,其底层是数组实现的,我们可以在集合中存储任意类型的数据,ArrayList是线程不安全的,非常适合用于对元素进行查找,效率非常高。 线程安全性 对ArrayList的操作一般分为两个步骤,改变位置(size)和操作元素(e)。所以这个过程在多线程的环境下是不能保证具有原子性的,因此A...

Java笔记虾 1356
上一篇: 八大基础数据结构
下一篇: LinkList源码分析
FLY翔仔
博客等级 码龄7年 1粉丝 8原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值