ArrayList源码万字解析!透彻易懂!

写在前面:我是「且听风吟」,目前是某上市游戏公司的大数据开发工程师,热爱大数据开源技术,喜欢分享自己的所学所悟,现阶段正在从头梳理大数据体系的知识,以后将会把时间重点放在Spark和Flink上面。

如果你也对大数据感兴趣,希望在这个行业一展拳脚。欢迎关注我,我们一起努力,一起学习。博客地址:https://ropledata.blog.csdn.net

博客的名字来源于:且听风吟,静待花开。也符合我对技术的看法,想要真正掌握一门技术就需要厚积薄发的毅力,同时保持乐观的心态。

你只管努力,剩下的交给时间!

图片

一、前言

不管是什么样的软件岗位,想要进大厂,肯定会问到一些基础源码知识,如果我们想要从中脱颖而出,就必须要非常熟悉这些源码。本文我们结合源码用通俗易懂的语言来解析ArrayList,尽量给每一行源码都写上注释,给每一个功能加上总结,由于内容较多,强烈建议收藏,那么废话不多说,一起来见真章!
源码开始

二、重新认识ArrayList

  1. 什么是ArrayList?
    ArrayList是基于数组实现的List类,封装了一个动态再分配的Object数组,以达到可以动态增长和缩减的索引序列。
  2. 长啥样?
    ArrayList
    如图,是一个长度为6,存储了6个6的数组,下标(index)从0开始,数组(elementData)表示存储的数据本身。
  3. 有哪些基本概念?
    • index 表示数组下标;
    • elementData 表示数组本身;
    • DEFAULT_CAPACITY 表示数组初始大小,默认是10;
    • size 表示当前数组的大小,int类型,未使用volatile修饰,非线程安全;
    • modCount 表示当前数组被修改的版本的次数,也就是说当数组结构有变动,就会+1。

三、知其所以然----撸源码

1. 从整个ArrayList.java的类注释开始入手

简单翻译总结如下:
- ArrayList允许put null值,并且会自动扩容;
- size,isEmpty,get,set,Iterator和listIterator方法的时间复杂度是O(1),add方法的复杂度根据添加元素的多少而不同,添加n个元素,时间复杂度O(n);
- 非线程安全,因此在多线程情况下,为了线程安全推荐使用(List list = Collections.synchronizedList(new ArrayList(...)););
- 支持增强for循环,但需要注意的是,由于非线程安全,所以在使用迭代器迭代过程中,如果数组大小被改变,会快速失败,并抛出异常。

2. 初始化
  1. 三种初始化方式,源码分析

    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) {
             
      	//elementData 是保存数组的容器,默认为 null
      	elementData = c.toArray();
      	//如果给定的集合(c)数据有值,则进行拷贝赋值操作
      	if ((size = elementData.length) != 0) {
             
      	  // c.toArray might (incorrectly) not return Object[] (see 6260652)
      	  //如果集合元素类型不是 Object 类型,才开始拷贝,否则不执行
      	  if (elementData.getClass() != Object[].class) {
             
      		elementData = Arrays.copyOf(elementData, size, Object[].class);
      	  }
      	} else {
             
      	  // 给定集合(c)无值,则默认空数组
      	  this.elementData = EMPTY_ELEMENTDATA;
      	}
        }
      
  2. 需要注意的地方

    1. 使用无参数构造器初始化时,默认容量时0,而不是传统印象里的10,只有接着对其进行add时才会变成10;
    2. 通过观察ArrayList的无参构造器,会发现当它进行初始化时,默认大小是空数组;
    3. 可以发现在指定初始数据初始化这种方式里,有一个see 6260652的注释,这是一个1.8版本的bug,在1.9版本的到了解决。解释是(toArray方法可能不会返回Object数组),也就是说当我们有一个ArrayList,里面的元素不是Object类型,比如是String,然后当我们调用toArray方法,得到一个Object数组后,再往这个String类型的Object数组赋值时,就会触发这个BUG,报错。官方查看文档地址:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6260652
3. 新增元素及扩容
  1. 先来看下新增元素核心源码
      public boolean add(E e) 
  • 63
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 47
    评论
评论 47
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值