【Java集合】系列一:详解ArrayList的底层原理(本篇源码基于Java11)

Java集合框架底层源码 集合底层源码 Collection List ArrayList public abstract class AbstractList<E>{ //操作数 protected transient int modCount = 0;//1 } ​ public class ArrayList<E> extends AbstractList<E> implements List<E>{ //空数组的实例(长度为0的数组实... 阅读详情

引言

ArrayList集合类在面试、开发中饱受关注,用起来也是真香。本篇文章有针对性的归纳整理ArrayList的常见问题,如有遗漏,欢迎留言或评论。

面试开始

小伙子,说下ArrayList的底层数据结构吧?

ArrayList的底层数据结构就是一个数组,数组元素的类型为Object类型,对ArrayList的所有操作底层都是基于该数组的。

程序清单1: ArrayList的底层数组

transient Object[] elementData; 

由源码可以看出,底层是个Object类型的数组,并且是用关键字transient修饰的,表示数组不可被序列化。

那结合刚刚说的底层数组实现说下ArrayList有哪些优缺点?

  • ArrayList的优点
  1. ArrayList底层以数组实现,是一种随机访问模式,再加上它实现了RandomAccess接口,因此查找也就是get的时候非常快。
  2. ArrayList在顺序添加一个元素的时候非常方便,只是往数组里面添加了一个元素而已。
  3. 根据下标遍历、访问元素,效率高。
  4. 可以自动扩容,默认为每次扩容为原来的1.5倍。
  • ArrayList的缺点
  1. 插入和删除元素的效率较低。
  2. 根据元素下标查找元素需要遍历整个元素数组,效率不高。
  3. 线程不安全。

好,刚刚你有提到扩容,可以说下ArrayList的扩容机制吗?

主要从构造函数和add()方法扩容两个层面进行分析。

程序清单2: ArrayList的构造函数源码

   private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
   
   public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    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);
        }
    }

由上述源码可发现,当用无参构造初始化ArrayList时,默认初始化为一个空的数组。

程序清单3: ArrayList的扩容源码

   public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    
    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }
    
    private Object[] grow() {
        return grow(size + 1);
    }
    
    /* 扩容代码 */
    private Object[] grow(int minCapacity) {
        /* 集合扩容完成后,需要将旧集合中的元素全部复制到新集合中 */
        return elementData = Arrays.copyOf(elementData,
                                           newCapacity(minCapacity));
    }
    
    /* 新的容量 */
    private int newCapacity(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        /* 扩容为1.5倍 */
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity <= 0) {
            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                /* 返回默认大小(10)和扩容后的大小的最大值 */
                return Math.max(DEFAULT_CAPACITY, minCapacity);
            if (minCapacity < 0) // overflow
                throw new OutOfMemoryError();
            return minCapacity;
        }
        return (newCapacity - MAX_ARRAY_SIZE <= 0)
            ? newCapacity
            : hugeCapacity(minCapacity);
    }

由上述源码可发现,当添加元素时每次都会校验数组大小。当初始化一个空的集合时,第一次add元素时集合的大小会被初始化为10。然后随着集合元素不断增加,当第11个元素插入时,这个时候集合需要扩容,扩容后的容量就是10+10>>1=15。扩容完成后,需要将员集合的元素复制到新的集合中。

注意:根据以上源码分析可以知道,ArrayList每次扩容都会在堆内存里开辟一个新的集合空间,将旧的集合中的所有元素都拷贝到新的集合中,旧的集合等待JVM回收。实际上这种不断复制需要内存和时间开销,所以最好在ArrayList初始化的时候就指定容量,并尽量保证之后不会扩容。

ArrayList是线程安全的么?为什么?

ArrayList是线程不安全的。体现在add()方法和迭代器里,具体的这里将结合源码说明。

来呀,先给国师上源码:

程序清单4: ArrayList的add()源码

    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    
    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }

注意看modCount++(该成员变量记录着ArrayList的修改次数)这里,我们知道自增、自减操作都是非原子操作,并发条件下必然有安全性问题。

add(e, elementData, size)这个方法的作用就是将当前的新元素加到列表后面,ArrayList底层数组的大小是否满足,如果size 长度等于底层数组的长度,那么就要对这个数组进行扩容。注意看,这个方法是没有任何的线程安全性保障的,假设现在ArrayList只剩一个元素可以添加了,此时线程1判断无需扩容进行正常添加操作;当添加尚未完成时,线程2也进入到这里,进行判断,也是返回还剩一个元素可以添加(但实际上这个位置已经被线程1占用了),此时线程2操作会报数组越界异常:ArrayIndexOutOfBoundsException。

程序清单5: ArrayList的迭代器源码

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                SubList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = root.modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
        
        final void checkForComodification() {
            if (root.modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }

注意看checkForComodification()方法的实现,会对ArrayList的modCount参数(该成员变量记录着ArrayList的修改次数)进行判断,如果实际修改次数和预期修改次数expectedModCount不一致(并发条件下会出现),则会抛出并发修改异常ConcurrentModificationException。

怎样让ArrayList成为线程安全的呢?

有两种方案:

  1. 使用synchronized关键字
  2. 创建ArrayList对象的时候采用Collections类中的静态方法synchronizedList(new ArrayList<>())

ArrayList、Vector和LinkedList的区别?

数据结构线程安全性增删改查的效率
ArrayList数组线程不安全查询快,增删慢(在末尾增删除外)
LinkedList双向链表线程不安全查询慢,增删快
Vector数组线程安全查询快,增删慢(在末尾增删除外)

面试结束

好的,小伙子表现不错,对ArrayList的掌握较好,下轮面试再见!悄悄给你透露一下哦,下一轮面试官喜欢考察HashSet,回去好好备战!

好嘞,谢谢面试官的提醒,下一回合我也会再接再厉。

利用wolcmd.exe批处理执行远程唤醒 代码如下: @echo off cls :check if exist wolcmd.exe goto targetmac echo 缺少wolcmd.exe echo. goto end :targetmac cls echo -------------------------------------------------------- echo 目标主机PC-01地址:40235678A... 阅读详情

相关推荐

ZYNQ裸机双网口通信实战:手把手教你用SDK和LWIP配置PS+PL以太网(附完整源码)

本文详细介绍了在ZYNQ裸机环境下实现双网口通信的全流程,包括硬件配置、LWIP协议栈优化及TCP通信实现。通过Vivado SDK和LWIP协议栈,开发者可以高效配置PS端内置网口与PL端扩展网口,提升嵌入式网络应用的灵活性和性能。附完整源码,助力快速开发。

weixin_30831529的博客 421

ArrayList集合底层原理

​ ArrayList是List接口的可变数组的实现。实现了所有可选列表操作,并允许包括 null 在 内的所有元素。每个 ArrayList 实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它是至少等于列表的大小。随着向 ArrayList 中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造 ArrayList 时 指定其容量。

weixin_43811057的博客 1227

Matlab实现PSO-TCN-LSTM-Attention粒子群算法优化时间卷积长短期记忆神经网络融合注意力机制多变量时间序列预测的详细项目实例

这些数据包含了随时间变化她动态信息,具有显著她趋势她、周期她和季节她特征。该模型她核心优势在她其能够自适应地学习数据中她多层次时序信息,捕捉时间序列中她长短期依赖关系,从而提高预测她准确她和可靠她。本项目旨在实她一个基她PTO-TCN-LTTM-TFSttfntion她多变量时间序列预测模型,通过粒子群优化算法优化时间卷积长短期记忆神经网络,并融合注意力机制,提高模型她预测精度和稳定她。本项目模型她实她分为多个阶段,首先她数据预处理,接着她PTO优化,然后她TCN和LTTM网络她构建她训练。

优质创作者: 人工智能技术领域 对于有一定基础的 MATLAB和Python带具体需求都可以详谈 MATLAB和Python可以提供代码调试服务加油 谢谢 你的鼓励是我前行的动力 谢谢支持 加油 谢谢 1080

ArrayList底层原理、线程安全及其相关集合(面试常问)

在面试中经常会问到一些ArrayList的底层原理、线程安全以及ArrayList相关集合等问题,现做一个总结方便自己日后复习也供各位小伙伴参考学习,如有什么问题欢迎指正。

weixin_59547613的博客 2959

简单搭建 可视化日志+链路追踪

JAVA 可视化日志、链路追踪、plumeLog、Tlog、Logback

qq_39535439的博客 1204

Python处理高光谱数据#3 降维

基于Python的高光谱降维操作 高光谱数据:点击此处可下载 用到的库:matplotlib、scipy、spectral 主要内容:主成分分析(PCA)与线性判别(LDA) 欢迎有兴趣的朋友交流指点。最后,废话不多说直接上代码???? import matplotlib.pyplot as plt from scipy.io import loadmat import spectral as spy

qq_273058791的博客 6936

ArrayList集合的底层原理

1.ArrayList的扩容机制在什么时候触发?是在集合中的元素超过原来的容量时触发。2.ArrayList的扩容因子是1.5,扩容为原来的1.5倍。

m0_54102377的博客 204

DNN加速器脉动阵列轻量级瞬时故障检测

本文系统综述了面向深度神经网络(DNN)脉动阵列加速器的轻量级瞬时故障检测技术。通过分析比较算法级校验(ABFT)、数值异常监测(剪裁、中值滤波等)、硬件编码检错(AN码、剩余码)以及选择性冗余等方案,指出这些方法能以5%-10%的开销实现对权重和激活比特翻转的高效检测,远低于传统ECC/冗余计算的成本。研究显示,结合多种轻量技术可构建多层防护体系,在保障推理可靠性的同时保持加速器的高性能特性。该成果为安全关键领域的DNN加速器设计提供了重要参考。

欢迎交流! 1074

Arraylist 与 LinkedList 区别

是否保证线程安全 ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全 底层数据结构 Arraylist 底层使用的是 Object 数组; LinkedList 底层使用的是双向链表数据结构(JDK1.6之前为循环链表,JDK1.7取消了循环。注意双向链表和双向循环链表的区别) 插入和删除是否受元素位置的影响 ① ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行 add(E e) 方法的时候, ArrayList 会默认在将指定的

Kirito的博客 724

100天精通Python(基础篇)——第4天:数据类型全解析_从基础到转换

数据类型全解析_从基础到转换

努力让自己发光,对的人才能迎着光而来 11万+

ArrayList集合-底层原理-浅分析

在java8中,如果通过无参构造创建ArrayList对象,第一次添加元素时,数组长度会由原长度0扩充到10,后续满容后再添加元素,扩充到原数组长度的1.5倍。有参构造,如果传入的参数为0,则第一添加元素,数组长度为1,后续满容后扩充原则为原数组长度的1.5倍。...

qq_47139621的博客 809

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Java之集合——详细概括和ArrayList的底层源码解析

目录一、集合的家族谱1.List2. Set3.Map4.Collection接口的方法(List、Set实现类通用)5.Map接口实现方法(Map实现类通用)二、ArrayList底层原码的详解1.前菜 2.底层原码解析示例代码(1)概况​编辑 (2)源码用到的常量和变量(3)分步源码详解A. ArrayList list = new ArrayList<>();B.第一层for循环 list.add(0);C.剩下九层循环list.add(i);

2202_75483664的博客 1225

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

【Java集合】单列集合List详解

List接口属于单列集合Collection接口下的其中一条分支,它最常用的三个实现类分别是——。这三个实现类各有各的特点,但又有着一些相同的特性。本文将从List集合的介绍、特点、使用,再到它的三个常用实现类的底层源码分析,完完整整地梳理清楚List位于,是单列集合Collection接口下的一个子接口。接口不能直接被实例化,因此需要通过创建其子类对象来完成该接口的实例化(即多态List 接口最常用的3个子类有。本文以ArrayList类为例演示List接口中的常用方法。

m0_55908255的博客 1085

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Java集合系列之ArrayList底层实现原理

  1. ArrayList是一个动态数组,实现了List&lt;E&gt;, RandomAccess, Cloneable, java.io.Serializable,并允许包括null在内的所有元素。 1.1,实现了RandomAccess接口标识着其支持随机快速访问,实际上,我们查看RandomAccess源码可以看到,其实里面什么都没有定义.因为ArrayList底层是数组,那么...

xujing_2017的博客 7514

Java集合框架之ArrayList解析

本文深入解析Java中ArrayList与Vector的底层数据结构、扩容策略及线程安全机制,通过源码分析揭示其性能差异与适用场景,探讨ArrayList的高效随机访问与Vector的同步代价,助你掌握集合类的核心原理与优化技巧。

m0_73941339的博客 966

一文让你清楚ArrayList和LinkedList有什么区别 以及 他们的底层原理、源码解析

在Java开发中,ArrayList和LinkedList是最比较的两种集合类型。它们虽然都实现了List接口,但底层实现和性能特点却大相径庭。了解它们的底层原理和适用场景,能够帮助我们在实际开发中做出更优的选择。本文将通过源码解析和性能对比,带你彻底理解二者的核心差异。ArrayList和LinkedList各有优劣,没有绝对的“更好”,只有“更适合”。理解它们的底层原理和性能特点,能够帮助我们在实际开发中根据具体需求做出合理选择。例如,实现一个需要快速随机访问的只读列表时,ArrayList是更优解。

一位想要成为优秀全栈工程师的有志青年! 1027

JUC并发—1.Java集合包底层源码剖析

此时再调用add()方法插入一个元素,也就是需要插入第11个元素,那么肯定是插入不进去的。虽然通过table[i]数组索引直接定位元素的时间复杂度是O(1),但如果链表存在大量元素,会是导致对该链表get()操作的性能急剧下降的。其中的分支代码"if ((p = tab[i = (n - 1) & hash]) == null)"的意思是:如果通过哈希寻址算法定位到的下标为i的数组元素为空(即tab[i]为空),那么就可以直接将一个新创建的Node对象放到数组的tab[i]这个位置。

mjunz的博客 1055

Java ArrayList扩容的原理

Java提供了Collection这个集合接口,可以用来作为数据的容器,其子接口分为单列集合List和双列集合Map,本文初略探索一下List集合下ArrayList的扩容原理。

Aishangyuwen的博客 1312

java集合的使用(包含源码分析、底层实现机制)

详细介绍了java中的常用集合,并且通过源码,分析了各个集合实现类的底层结构以及实现方式。HashMap、ArrayList、Vector等。

weixin_74200123的博客 979

Java面试实战系列【集合篇】-ArrayList源码深度解析

最简单的解释:ArrayList就是一个可以动态增长的数组。普通数组创建后大小就固定了,而ArrayList可以根据需要自动扩容。普通数组 = 固定大小的停车场,车位满了就不能再停车ArrayList = 智能停车场,车位不够时会自动扩建更多车位开始盘点前:记录当前的"借阅记录版本号"是V1.0盘点过程中:你一本本检查书籍检查机制:每次检查,你就看看借阅记录版本号是否还是V1.0发现问题:如果版本号变成了V1.1,说明有人在你盘点期间借了书或还了书立即停止。

weixin_53590296的博客 991

【Java零基础连载11】Java集合框架上篇|集合体系、ArrayList、LinkedList、Vector底层原理与实战

上一篇我们彻底吃透了String三兄弟、八大包装类、日期工具类等高频API,补齐了日常开发工具类短板,能够熟练完成字符串处理、数值转换、日期格式化等基础业务操作。从本篇开始,我们正式进入Java进阶核心重难点:集合框架。如果说基础语法是Java的骨架,那么集合就是Java的血肉。在实际项目开发中:查询列表数据、分页数据、批量参数、对象存储、临时数据缓存,99%的场景都离不开集合。

荏苒夕阳的博客 241

Java集合源码剖析——基于JDK1.8中ArrayList的实现原理

Java集合源码剖析——基于JDK1.8中ArrayList的实现原理

贝尔摩德苦艾酒 7584

「集合底层」ArrayList底层扩容机制及源码分析

ArrayList作为List接口的实现类,它是一种可以根据需要动态增长的数组。在Java中标准的数组都是定长的,当一个数组被创建后,它不能再被修改长度,也就是说我们在创建数组时要确定数组所需的长度。但有时我们需要动态程序中获取数组长度,此时,我们就可以使用ArrayList来存储数据,但是它并不是线程安全的。先说结论ArrayList中维护了一个Object类型的数组elementData.[debug看源码]transienttransient表示瞬间,短暂的,表示该属性不会被序列号。

frozenpenguin的博客 1767

【Java集合的使用以及部分底层实现】

无论前方等待我们的是什么,让我们保持勇气和决心,一路向前!

weixin_63309304的博客 1092
上一篇: 你想要的String面试的知识点,这里都有
下一篇: 【Java集合】系列二:搞定HashSet(本篇源码基于Java11)
少侠露飞
博客等级 码龄9年 214粉丝 117原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值