从源码分析java容器之ArrayList

引言

ArrayList在平常工作中是用的最多的容器之一,本文将从JDK8源码(相比JDK7有一点改变)对ArrayList进行分析

1、ArrayList结构图

从下面的图中,我们可以看到ArrayList继承自AbstractList,实现了 List 、RandomAccess、Cloneable、Serializable接口,所以ArrayList 是支持快速访问、复制、序列化的。
在这里插入图片描述

2.分析源码

分析的顺序是从对象的属性,构造方法,常用方法进行分析

2.1、属性

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
	//默认容量,即为初始值大小
    private static final int DEFAULT_CAPACITY = 10;
	//共享的空数组,用于初始化空实例
    private static final Object[] EMPTY_ELEMENTDATA = {};
	//共享的空数组,用于初始化空实例(这个属性是JDK8新增的)
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	//ArrayList内部结构,是一个Object[]类型的数组,用transient修饰
    private transient Object[] elementData;
    //数组长度大小   
    private int size;
    //...省略部分代码
}

2.2、构造方法

	//默认的构造方法,它将创建一个空数组
	public ArrayList() {
   		this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
	}
	//表示接受指定地容量值,初始化创建数组,建议在可估算数组大小时,创建ArrayList可指定
	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);
        }
	}
    //接收一个Collection的实体,将该Collection实体转换为ArrayList对象
    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;
        }
    }

2.3、add方法

public boolean add(E e) {
		//扩容方法
	    ensureCapacityInternal(size + 1);
	    //将添加的元素放到size++位置上,注意该步骤是非线程安全的 
        elementData[size++] = e;
        return true;
}
2.3.1、扩容
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//判断初始化是否使用默认构造方法(EMPTY_ELEMENTDATA是初始化容量为0,DEFAULTCAPACITY_EMPTY_ELEMENTDATA使用默认构造方法,没有初始化容量)
//若是,则设置初始化大小为默认的值10,否则使用传入的参数。
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++;
		//若长度大于数组长度,则扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
}

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//标记3
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //新数组大小为1.5倍原数组长度
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //若值newCapacity比传入值minCapacity还要小,则使用传入minCapacity
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //若newCapacity比设定的最大数组容量大,则使用最大整数值
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //复制原来的数组到新数组,并将引用地址指向新数组对象
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

3、总结

  1. ArrayList是基于数组实现的,它的内存储元素的数组为 elementData;elementData的声明为:transient Object[] elementData;
  2. ArrayList中EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA的使用;这两个常量,使用场景不同。前者是用在用户通过ArrayList(int initialCapacity)该构造方法直接指定初始容量为0时,后者是用户直接使用无参构造创建ArrayList时。
  3. ArrayList的扩容计算为 newCapacity = oldCapacity + (oldCapacity » 1),大概是1.5倍,且不会无限扩容。

结束语

限于篇幅,本篇只详细分析ArrayList里面的add方法源码,对于开发人员来讲,最重要的就是记住初始化的ArrayList的时候一定要指定初始容量,否则扩容非常消耗性能,其次就是ArrayList是非线程安全的。

如果本篇的分析对大家有用,请大家点一个赞!

原创文章 55 获赞 76 访问量 17万+

猜你喜欢

转载自blog.csdn.net/cool_summer_moon/article/details/105659613