目录

属性

构造函数

无参构造函数

含参构造(int initialCapacity)

含参构造(Collection c)

add方法

add(E e)

add(int index, E element)

addAll

addAll(Collection c)

addAll(int index, Collection c)

get函数

set函数

remove函数

remove(int index)

remove(Object o)

clear函数

缩容函数--trimToSize


扒开源码,我们首先看到的就是ArrayList类继承关系

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

继承 AbstractList<E> 抽象类,实现了List接口,支持随机访问,实现 Cloneable,Serializable 支持克隆和序列化功能。接下来我们从源码角度逐一分析:

属性

    // 默认容量大小 10 private static final int DEFAULT_CAPACITY = 10;// 空的共享数组,和DEFAULTCAPACITY_EMPTY_ELEMENTDATA区别是知道元素添加后如何扩容private static final Object[] EMPTY_ELEMENTDATA = {};// 空的共享数组private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};// 不参与序列化的elementData数组来存放ArrayList元素transient Object[] elementData; // non-private to simplify nested class access// 数组元素个数private int size;

构造函数

无参构造函数

    public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}

构造一个初始容量为10的空数组列表,即当我们默认不提供参数new一个ArrayList数组时,源码底层会默认按照10的长度空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 给我们实例化。有人也许有疑问,DEFAULTCAPACITY_EMPTY_ELEMENTDATA 没有指定数值,为什么说初始化一个长度为10呢?后面我们分析 add() 函数会详细解释。

含参构造(int initialCapacity)

    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);}}

指定容量的构造函数,首先判断容量大小,对于initialCapacity 大于0情况,new一个指定容量大小的数组赋值给elementData数组;当initialCapacity等于0用空的常量数组来实例化elementData数组;小于0,直接抛出异常。

含参构造(Collection<? extends E> c)

    public ArrayList(Collection<? extends E> 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;}}

将容器Collection转化为数组赋给数组elementData,还对Collection转化是否转化为了Object[]进行了检查判断。如果Collection为空,则就将空的常量数组对象EMPTY_ELEMENTDATA赋给了elementData;

add方法

add(E e)

    public boolean add(E e) {//确保容量够大ensureCapacityInternal(size + 1);  // Increments modCount!!elementData[size++] = e;return true;}

首先检查容量是否够大,然后将元素添加到数组末尾,数组size +1。

    private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}//得到最小扩容量private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {// 获取默认的容量和传入参数的较大值return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity;}//判断是否需要扩容private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)//调用grow方法进行扩容,调用此方法代表已经开始扩容了grow(minCapacity);}

从源码可以看出,如果elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA,则就用默认容量10来进行开辟空间。这里的源码就解释了DEFAULTCAPACITY_EMPTY_ELEMENTDATA数组和EMPTY_ELEMENTDATA数组的区别之所在。也给出了当我们用无参构造函数来实例化一个对象时,确实是构造的一个长度为10的数组对象。

扩容函数-grow

    //ArrayList扩容的核心方法。private void grow(int minCapacity) {//oldCapacity为旧容量,newCapacity为新容量int oldCapacity = elementData.length;//将oldCapacity 右移一位,其效果相当于oldCapacity /2,//我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,int newCapacity = oldCapacity + (oldCapacity >> 1);/*下面两个if的作用为处理两种情况:1)第一种情况为:如果newCapacity扩展的过小。则应该至少扩张到所需的空间大小minCapacity2)第二种情况为:newCapacity扩张的过大,如果过大,则用Integer.MAX_VALUE来代替。*/if (newCapacity - minCapacity < 0)newCapacity = minCapacity;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.5倍。
但是,由于扩张1.5倍可能和我们的需要不一致,即可能太小,也可能太大。因此,就有了源码中的两个if条件的处理。即如果扩张1.5倍太小,则就用我们需要的空间大小minCapacity来进行扩张。如果扩张1.5倍太大或者是我们所需的空间大小minCapacity太大,则进Integer.MAX_VALUE来进行扩张。

从上面 grow() 方法源码我们知道: 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) hugeCapacity() 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,如果minCapacity大于最大容量,则新容量则为Integer.MAX_VALUE,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 Integer.MAX_VALUE - 8。

    private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();//对minCapacity和MAX_ARRAY_SIZE进行比较//若minCapacity大,将Integer.MAX_VALUE作为新数组的大小//若MAX_ARRAY_SIZE大,将MAX_ARRAY_SIZE作为新数组的大小//MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE :MAX_ARRAY_SIZE;}

add(int index, E element)

在指定索引位置,添加元素。

    public void add(int index, E element) {//检查索引是否合法rangeCheckForAdd(index);//确保容量大小ensureCapacityInternal(size + 1);  // Increments modCount!!//arraycopy()方法实现数组自己复制自己//elementData:源数组;index:源数组中的起始位置;elementData:目标数组;index + 1:目标数组中的起始位置; size - index:要复制的数组元素的数量;System.arraycopy(elementData, index, elementData, index + 1,size - index);elementData[index] = element;size++;}

addAll

addAll(Collection<? extends E> c)

    public boolean addAll(Collection<? extends E> c) {Object[] a = c.toArray();int numNew = a.length;ensureCapacityInternal(size + numNew);  // Increments modCount//将a中的所有元素拷贝到数组elementData最后一个元素的后面System.arraycopy(a, 0, elementData, size, numNew);size += numNew;return numNew != 0;}

addAll(int index, Collection<? extends E> c)

在指定的位置添加集合元素

    public boolean addAll(int index, Collection<? extends E> c) {//检查下标是否合法rangeCheckForAdd(index);Object[] a = c.toArray();int numNew = a.length;//检测容量是否够ensureCapacityInternal(size + numNew);  // Increments modCountint numMoved = size - index;//是否在尾部添加if (numMoved > 0)System.arraycopy(elementData, index, elementData, index + numNew,numMoved);System.arraycopy(a, 0, elementData, index, numNew);size += numNew;return numNew != 0;}

get函数

    public E get(int index) {//检测下标是否合法rangeCheck(index);return elementData(index);}

set函数

这个是改变ArrayList对象中某个位置元素的值的大小。

    public E set(int index, E element) {//检测下标是否合法rangeCheck(index);//获取旧值E oldValue = elementData(index);elementData[index] = element;return oldValue;}

remove函数

remove(int index)

删除指定索引位置元素,自身拷贝(即将数组从index+1位置开始到末尾的元素拷贝到从index开始处)

    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;}

remove(Object o)

删除指定值的元素

    public boolean remove(Object o) {if (o == null) {for (int index = 0; index < size; index++)if (elementData[index] == null) {fastRemove(index);return true;}} else {for (int index = 0; index < size; index++)if (o.equals(elementData[index])) {fastRemove(index);return true;}}return false;}

从源码中可以看到,无论是指定对象o是否为null,都是在ArrayList中找到与此第一个相等的元素的位置,然后调用fastRemove(index)来进行移除;如果没有找打指定对象o的位置,则返回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}

remove和fastRemove唯一的区别是fastRemove函数没有对index进行有效性检查,以及没有返回移除的旧值,为什么不返回呢?这是因为remove(Object o)给出的就是要删除的值,因此不返回旧值是非常正常的。

clear函数

清除所有元素值,直接将数组中的所有元素设置为null即可,这样便于垃圾回收。

    public void clear() {modCount++;// clear to let GC do its workfor (int i = 0; i < size; i++)elementData[i] = null;size = 0;}

缩容函数--trimToSize

将数组元素的大小缩减到实际size,通过判断size是否小于elementData大小,如果是则将elementData赋值给实际的大小数组。

    public void trimToSize() {modCount++;if (size < elementData.length) {elementData = (size == 0)? EMPTY_ELEMENTDATA: Arrays.copyOf(elementData, size);}}

接下来我们通过一个demo说明一下

public static void main(String[] args) {ArrayList<Integer> arrayList = new ArrayList<>();for (int i = 0; i < 10; i++) {arrayList.add(i);}System.out.println(getNum(arrayList));//10arrayList.add(11);System.out.println(getNum(arrayList));//15arrayList.trimToSize();System.out.println(getNum(arrayList));//11}
 //获取elementData数组长度public static int getNum(List arrayList){Class clazz = arrayList.getClass();try {Field field = clazz.getDeclaredField("elementData");field.setAccessible(true);Object[] l = (Object[]) field.get(arrayList);return l.length;} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}return -1;}

通过添加10个元素后,再添加1个元素,再调用缩容函数打印实际的elementData的长度,发现结果即是吧ArrayList缩减到实际长度。

Java集合源码系列(1)---- ArrayList详解相关推荐

  1. React 源码系列 | React Context 详解

    目前来看 Context 是一个非常强大但是很多时候不会直接使用的 api.大多数项目不会直接使用 createContext 然后向下面传递数据,而是采用第三方库(react-redux). 想想项 ...

  2. java反射源码_java反射技术详解附源码

    在学校学习Java时,由于学的不扎实,也没经历过太多实战项目,所以很多重要的知识点瞟一眼就过去了,比如现在要讲的反射,当时直接就忽略掉了,可现在发现很多地方需要反射,不得不重新学习一下,上学欠了太多债 ...

  3. react 遍历对象_React 源码系列 | React Children 详解

    本文基于 React V16.8.6,本文代码地址 测试代码 源码讲解 React 中一个元素可能有 0 个.1 个或者多个直接子元素,React 导出的 Children 中包含 5 个处理子元素的 ...

  4. Java集合源码解析之ArrayList

    uml类图: 基本简介: ArrayList的底层数据结构是数组,所以内存需要为arrayList保证有足够的连续的内存空间. 添加操作会导致数组扩容,数组扩容比较消耗性能. 非尾部的添加和删除元素操 ...

  5. Java集合源码分析(二)ArrayList

    ArrayList简介 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长,类似于C语言中的动态申请内存,动态增长内存. ArrayList不是线程安全的,只能用在单线程环境下,多线 ...

  6. Java集合源码浅析(一) : ArrayList

    (尊重劳动成果,转载请注明出处:https://yangwenqiang.blog.csdn.net/article/details/105418475冷血之心的博客) 背景 一直都有这么一个打算,那 ...

  7. 源码 解析_最详细集合源码解析之ArrayList集合源码解析

    从今天开始我会将集合源码分析陆陆续续整理,写成文章形成集合源码系列文章,方便大家学习 ArrayList集合源码其实相对比较简单,整个源码结构相对于HashMap等源码要好理解的多:先来看下Array ...

  8. Xposed源码剖析——app_process作用详解

    Xposed源码剖析--app_process作用详解 首先吐槽一下CSDN的改版吧,发表这篇文章之前其实我已经将此篇文章写过了两三次了.就是发表不成功.而且CSDN将我的文章草稿也一带>删除掉 ...

  9. mysql data文件夹恢复_【专注】Zabbix源码安装教程—步骤详解(2)安装并配置mysql...

    四.安装并配置mysql(1) 解压mysql-5.7.26.tar.gz与boost_1_59_0.tar.gz #tar -xvf mysql-5.7.26.tar.gz #tar -xvf bo ...

最新文章

  1. FortiGate基本信息
  2. python【力扣LeetCode算法题库】面试题 01.07- 旋转矩阵
  3. aliyun maven 添加jar_阿里云Maven配置,Maven仓库配置,Maven镜像配置
  4. 单元测试之JUnit 5 参数化测试使用手册
  5. 【数据库】Window环境安装MySQL Server 5.7.21
  6. C语言 | 输出魔方矩阵
  7. 福昕阅读器中删除单个,多个注释,隐藏所有注释。
  8. 微信公众号发红包 php,php微信公众号开发之现金红包
  9. mysql语句解决查询乱码_sql命令查询出现乱码的解决方法详解
  10. 分布滞后与自回归模型 ADL
  11. python安装os模块_python安装os
  12. Bellman 贝尔曼方程究竟是什么
  13. 澳洲移民监 盘点一下各国的移民监要求
  14. 单应性(Homography)变换
  15. CD19药物|适应症|市场销售-上市药品前景分析
  16. 京东开源asyncTool之线程编排
  17. 旋转角度如何知道是顺时针还是逆时针旋转?(仅供参考,更靠谱的是旋转轴到z轴正半轴上)
  18. 重庆:智能网联汽车驶入“快车道”,中国“底特律”走向复兴?
  19. 分布式搜索elasticsearch搜索功能【进阶】
  20. APP定制开发的几种付费模式

热门文章

  1. php彩色教程,Photoshop简单制作立体彩色炫图
  2. VScode添加C++万能头文件
  3. Android毕业设计及论文答辩经验分享
  4. haproxy负载均衡+pcs高可用+fence
  5. 各行各业1000个小程序源码
  6. 小程序代码包压缩 策略方案
  7. 二十几岁必须看透的50个人生谬误
  8. APP开发所需时间,看完这些你就懂了
  9. SVN本地目录创建及使用
  10. Cadence Pspice添加外部白噪声