ArrayList源码解析

336 阅读3分钟

JDK版本

jdk7的版本  jdk1.7.0_80

****jdk8的版本  1.8.0_131

ArrayList中的常量

  • int DEFAULT_CAPACITY = 10; 数组的默认长度

  • Object[] EMPTY_ELEMENTDATA = {}; 空数组

  • Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; 默认大小的空数组

  • Object[] elementData; 存储ArrayList元素的数组

  • int size; ArrayList的长度

  • int modCount = 0; 数组结构改变的次数

  • int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 数组需要8位去储存数组的长度2^31。 尝试分配更大的数组可能导致OutOfMemoryError:请求的数组大小超过VM限制

JDK7中的ArrayList

初始化

初始化时,使用有参构造器就创建elementData数组。使用无参构造器时就默认为{}。相对于我的jdk版本,这个7和8几乎没有区别

有的说jdk7中创建ArrayList是类似于单例模式中的饿汉式。不管传不传initialCapacity,都会初始化,我就找了段代码,别说还真是

使用无参构造器创建ArrayList时,会以默认容量10调用有参构造器创建Object数组。

JDK8中的ArrayList

初始化

初始化时,如果传入的initialCapacity大于0,就创建elementData数组。没有或使用无参构造器时就默认为{}

add方法

JDK1.8和1.7中add方法无太大区别

public boolean add(E e) {    
    ensureCapacityInternal(size + 1);  // 确认容量是否够用
    elementData[size++] = e; // 存储数据
    return true;
}

由于代码分布分散,不好截图,就直接描述

ensureCapacityInternal(size + 1) 方法先去判断elementData是否为{}(没有传入默认的数组长度,第一次调用add方法),就使用DEFAULT_CAPACITY初始化elementData,不为DEFAULTCAPACITY_EMPTY_ELEMENTDATA时就判断是否满足扩容条件,满足就扩容

if (minCapacity - elementData.length > 0) grow(minCapacity);

grow(int minCapacity)

  • 新扩容的数组长度为之前的1.5倍
  • 扩容后的数组不满足最小长度,就把最小长度赋值给新的数组长度
  • Arrays.copy()方法。复制指定的数组,截断或填充空值到指定长度。底层最终调用的还是System.arraycopy(); 

System.arraycopy(); 

额外写一下这个方法。在ArrayList中remove、addAll方法中都有这个方法的存在。

static native void arraycopy(Object src,int srcPos, Object dest, int destPos,int length);
  • src    源数组
  • srcPos  原数组中的起始位置
  • dest     目标数组
  • destPos   目标数组中的其实位置
  • length      要复制的源数组元素的数量

总结

jdk7(某些版本):初始化时,使用无参构造器创建ArrayList时,会以默认容量10调用有参构造器创建Object数组。当调用add方法导致底层elementData的数组容量不够,则扩容。默认情况下扩容为原来容量的1.5倍。同时将原有数组赋值到新的数组中

jdk8:初始化时,如果传入的initialCapacity大于0,就创建elementData数组。没有或使用无参构造器时就不创建数组,第一次调用add方法时,底层创建容量为10的数组。当调用add方法导致底层elementData的数组容量不够,则扩容。默认情况下扩容为原来容量的1.5倍。同时将原有数组赋值到新的数组中

jdk7和8的扩容方式几乎是一致的。

JDK7(某些版本)中的ArrayList的对象的创建方式就类似于单例模式中的饿汉式,jdk8中的创建方式就相当于懒汉式。延迟了数组的创建。节省内存。