编程语言
首页 > 编程语言> > ArrayList源码解析 基于JDK1.8

ArrayList源码解析 基于JDK1.8

作者:互联网

1.问题产生原因

最近在写leetcode的题目https://leetcode.cn/problems/subsets/时,在评论区看到了一种解法,其中出现了

List<Integer> newNumber = new ArrayList<>(result);
//result为List<Integer>类型

的代码语句,一般来说ArrayList的初始化为

List<Integer> some=new ArrayList<>();

即括号内没有参数,那如果里面有参数是什么意思。这需要我们去解读ArrayList的源码。

2.源码解析

2.1类声明

ArrayList继承了一个抽象类,实现了四个接口

图片

图片

分析:

2.2类属性

// 序列化自动生成的一个码,用来在正反序列化中验证版本一致性,一致可以进行反序列化
private static final long serialVersionUID = 8683452581122892189L;

/**
 * 默认初始容量大小为10
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * 静态共享空数组实例(用于空实例)
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * 静态共享空数组实例(用于默认大小空实例)
 * 将它和EMPTY_ELEMENTDATA区别开来,
 * 以便在添加第一个元素时知道数组容量需要扩容多少
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 保存ArrayList数据的数组,transient修饰此对象
 * ArrayList的容量是这个数组缓冲区的长度
 */
transient Object[] elementData; // non-private to simplify nested class access

/**
 * ArrayList当前元素个数,默认为 0
 */
private int size;

/**
 * 可以分配的最大容量
 * Integer.MAX_VALUE - 8 是因为数组中有一个额外的元数据,
 * 用于表示数组的大小(8bytes)
 * 强制分配可能会导致 OutOfMemoryError
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

图片

除上面的属性之外,还有一个经常用到的属性是从AbstractList继承过来的属性modCount,代表ArrayList集合的修改次数。

分析:

补充:JDK7无参构造初始化ArrayList对象时,直接创建了长度为10的 数组elementData

2.3构造函数

2.3.1 ArrayList()

无参构造函数

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

2.3.2 ArrayList(int initialCapacity)

带参构造函数,参数为用户指定的初始容量

/**
 * Constructs an empty list with the specified initial capacity.
 *
 * @param  initialCapacity  the initial capacity of the list
 * @throws IllegalArgumentException if the specified initial capacity
 *         is negative
 */
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);
    }
}

2.3.3 ArrayList(Collection<? extends E> c)

带参构造函数,参数为其他集合

/**
 * Constructs a list containing the elements of the specified
 * collection, in the order they are returned by the collection's
 * iterator.
 *
 * @param c the collection whose elements are to be placed into this list
 * @throws NullPointerException if the specified collection is null
 */
public ArrayList(Collection<? extends E> c) {
    //将集合转为 Object[] 类型数组
    elementData = c.toArray();
    //更新size的值,同时判断size的大小
    if ((size = elementData.length) != 0) {
        //c.toArray might (incorrectly) not return Object[] (see 6260652)
        //这里c.toArray 返回的可能不是 Object[],是因为继承:
        //所有继承Collection的类都可以重写toArray方法,
        //当子类重写父类同名的方法时,若返回值类型不一致,
        //默认调用的是子类的方法,所以可能导致返回的类型不为Object[]
        if (elementData.getClass() != Object[].class)
            //返回类型不为Object[]则使用copeof方法拷贝一份
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

2.3.4 错误例子

当我们运行下面的代码时,会报错

public static void main(String[] args) {
    ArrayList<String> list=new ArrayList<>(5);
    list.add(2,"xx");
}

图片

private void rangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

add首先调用rangeCheckForAdd,而此时size为0,index=2>size=0,所以出错,抛出异常

public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    ArrayList<String> list=new ArrayList<>(Arrays.asList(new String[5]));
    Class<ArrayList> aClass = ArrayList.class;
    Field field = aClass.getDeclaredField("elementData");
    field.setAccessible(true);
    Object[] objects = (Object[]) field.get(list);
    
    System.out.println("list:"+list);
    System.out.println("size:" + list.size());
    System.out.println("length:" + objects.length + " elementData:" + Arrays.toString(objects));
    
    list.add(2,"xx");
    System.out.println("after add");
    field = aClass.getDeclaredField("elementData");
    field.setAccessible(true);
    objects = (Object[]) field.get(list);
    
    System.out.println("list:"+list);
    System.out.println("size:" + list.size());
    System.out.println("length:" + objects.length + " elementData:" + Arrays.toString(objects));
}

使用集合构造ArrayList时,size被赋值为elementData.length,所以输出为5,再执行add函数,不会出错,并且list的size加1,变为6,而ArrayList的容量变为5*1.5=7
图片

2.4扩容机制函数

以无参构造函数创建的 ArrayList 为例分析

2.4.1 add(E e)

添加特定的元素到集合末尾

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return <tt>true</tt> (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    //尝试容量加1,检查是否需要扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //将元素插入到最后一位,线程不安全
    elementData[size++] = e;
    return true;
}

JDK11 移除了ensureCapacityInternal() 和 ensureExplicitCapacity() 方法

2.4.2 ensureCapacityInternal(int minCapacity)

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

2.4.3 calculateCapacity(Object[] elementData, int minCapacity)

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}

2.4.4 ensureExplicitCapacity(int minCapacity)

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    //修改次数 +1,用于 fail-fast 机制

    //当所需要的最小容量大于elementData的长度时才进行扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

2.4.5 grow(int minCapacity)

/**
 * Increases the capacity to ensure that it can hold at least the
 * number of elements specified by the minimum capacity argument.
 *
 * @param minCapacity the desired minimum capacity
 */
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //将oldCapacity 右移一位,其效果相当于oldCapacity /2,
    //整句运算式的结果就是将新容量更新为旧容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //检查新容量是否大于最小需要容量,
    //若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //如果新容量大于MAX_ARRAY_SIZE,
    //执行hugeCapacity方法比较minCapacity 和 MAX_ARRAY_SIZE,
    //如果minCapacity大于最大容量,则新容量为Integer.MAX_VALUE,
    //否则,新容量为MAX_ARRAY_SIZE即为Integer.MAX_VALUE - 8
    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 右移一位相当于除 2,右移 n 位相当于除以 2 的 n 次方。这里 oldCapacity右移了1位所以相当于 oldCapacity /2。对于大数据的 2 进制运算,位移运算符比那些普通运算符的运算要快很多,因为程序仅仅移动一下而已,不去计算,这样提高了效率,节省了资源

2.4.6 hugeCapacity(int minCapacity)

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

2.4.7 扩容总结

//JDK 1.7
public ArrayList(int initialCapacity) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
}



public ArrayList() {
    this(10);
}

2.5 System.arraycopy()和Arrays.copyOf()方法

2.5.1 System.arraycopy()

源码

//arraycopy 是一个 native 方法
/**
* @param src 源数组
* @param srcPos 源数组中的起始位置
* @param dest 目标数组
* @param destPos 目标数组中的起始位置
* @param length 要复制的数组元素的数量
*/
public static native void arraycopy(Object src,  int  srcPos,
                                    Object dest, int destPos,
                                    int length);

在ArrayList中的应用

/**
 *在此列表中的指定位置插入指定的元素
 */
public void add(int index, E element) {
    // 调用 rangeCheckForAdd 对 index 进行界限检查
    rangeCheckForAdd(index);
    // 保证容量足够
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //自己复制自己,使 index 之后的元素全部向后移一位
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    //将element插入到index位置
    elementData[index] = element;
    size++;
}

测试

public static void main(String[] args) {
    int[] a=new int[5];
    a[0]=12;
    a[1]=2;
    System.arraycopy(a,1,a,2,2);
    a[1]=88;
    for(int i:a){
        System.out.print(i+" ");
    }
}

图片

2.5.2 Arrays.copyOf()

源码

public static int[] copyOf(int[] original, int newLength) {
    //申请一个新的数组
    int[] copy = new int[newLength];
    //调用System.arraycopy,将源数组中的数据进行拷贝,并返回新的数组
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

在ArrayList中的应用

public Object[] toArray() {
    //elementData:要复制的数组;size:要复制的长度
    return Arrays.copyOf(elementData, size);
}

测试

public static void main(String[] args) {
    int[] a=new int[5];
    a[0]=12;
    a[1]=2;
    a[2]=88;
    a[3]=6;
    a[4]=55;
    int[] b= Arrays.copyOf(a,4);
    for(int i:b){
        System.out.print(i+" ");
    }
    System.out.println(b.length);
    int[] c=Arrays.copyOf(a,10);
    for(int i:c){
        System.out.print(i+" ");
    }
    System.out.println(c.length);
}

图片

可以简单认为,使用 Arrays.copyOf()方法主要是为了改变原有数组的容量

2.5.3 两者对比

2.6 ensureCapacity(int minCapacity)

 /**
 * Increases the capacity of this <tt>ArrayList</tt> instance, if
 * necessary, to ensure that it can hold at least the number of elements
 * specified by the minimum capacity argument.
 *
 * @param   minCapacity   the desired minimum capacity
 */
public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
        // any size if not default element table
        ? 0
        // larger than default for default empty table. It's already
        // supposed to be at default size.
        : DEFAULT_CAPACITY;

    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}
public static void main(String[] args) {
    ArrayList<Object> list = new ArrayList<>();
    final int N = 10000000;
    long startTime = System.currentTimeMillis();
    for (int i = 0; i < N; i++) {
        list.add(i);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("使用ensureCapacity方法前:"+(endTime - startTime));
}

图片

public static void main(String[] args) {
    ArrayList<Object> list = new ArrayList<>();
    final int N = 10000000;
    long startTime = System.currentTimeMillis();
    list.ensureCapacity(N);
    for (int i = 0; i < N; i++) {
        list.add(i);
    }
    long endTime = System.currentTimeMillis();
    System.out.println("使用ensureCapacity方法后:"+(endTime - startTime));
}

图片

3.总结

4.主要参考博客

https://javaguide.cn/java/collection/arraylist-source-code.html#_3-4-ensurecapacity%E6%96%B9%E6%B3%95

https://juejin.cn/post/6844903582194466824#heading-13

https://www.jianshu.com/p/97874e3384ab

https://www.cnblogs.com/zhangyinhua/p/7687377.html

https://cloud.tencent.com/developer/article/1792774

https://www.cnblogs.com/ZhaoxiCheung/p/Java-ArrayList-Source-Analysis.html

https://blog.csdn.net/yuzhangzhen/article/details/108822997

https://blog.csdn.net/qq_35387940/article/details/123050667?spm=1001.2101.3001.6650.13&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-13-123050667-blog-102522476.pc_relevant_multi_platform_whitelistv2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-13-123050667-blog-102522476.pc_relevant_multi_platform_whitelistv2&utm_relevant_index=17

https://blog.csdn.net/Tian_ttt/article/details/107138504?spm=1001.2101.3001.6650.14&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-14-107138504-blog-102522476.pc_relevant_multi_platform_whitelistv2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-14-107138504-blog-102522476.pc_relevant_multi_platform_whitelistv2&utm_relevant_index=18

标签:JDK1.8,int,minCapacity,ArrayList,elementData,源码,数组,size
来源: https://www.cnblogs.com/ycylikestudy/p/16441419.html