【源码解析】ArrayList源码解析

存储特性 – 有序且可重复

  • 存储元素,底层 Object 数据,数组不会对元素做判断,所以可重复,基于数组下标的连续存储,所以有序
  • 数组容量一旦定义,就不能更改,可以扩容

初始容量 – 默认情况下,arrayList初始大小为 0

存储满后 – 扩容大小是原来的1.5倍

源码add方法

public class ArrayList<E> extends AbstractList<E> 
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    // 版本号
	private static final long serialVersionUID = 8683452581122892189L;
	//设置arrayList默认容量   JDK1.6版本之前 10   1.6版本之后初始化大小为0
	private static final int DEFAULT_CAPACITY = 10;
	//空数组,当调用无参数构造函数的时候默认给个空数组
	private static final Object[] EMPTY_ELEMENTDATA = {};
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	//这才是真正保存数据的数组
	private transient Object[] elementData;
	//arrayList的实际元素数量
	private int size;
    
	//构造方法传入默认的capacity 设置默认数组大小
	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);
        }
    }
    
    // 无参数构造方法默认为空数组
    // 当前arrayList初始化构建的时候,底层的object数组长度为0
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    
    //构造方法传入一个Collection, 则将Collection里面的值copy到arrayList
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        size = elementData.length;
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    }
    
    //下面主要看看ArrayList 是如何将数组进行动态扩充实现add 和 remove
    public boolean add(E e) {
        // size + 1 计算当前存储元素所需的数组最小长度
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    private void ensureCapacityInternal(int minCapacity) {
        // 第一次调用 calculateCapacity(elementData, minCapacity) = 10
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        // elementData 空数组,长度0
        // 超出了数组可容纳的长度,需要进行动态扩展
        if (minCapacity - elementData.length > 0)
            // arrayList扩容实现 minCapacity = 10
            grow(minCapacity);
    }
    
    // 传递 minCapacity = 10
    private void grow(int minCapacity) {
        // overflow-conscious code
        // 获取数组原本长度
        int oldCapacity = elementData.length;
        // oldCapacity 右移一位,第一次执行,数组长度0,所以还是0
        // 不是第一次执行,oldCapacity >> 1 == 10(1010) >> 1  == 5(101)
        // 设置新数组的容量扩展为原来数组的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        // 再判断一下新数组的容量够不够,够了就直接使用这个长度创建新数组,
        // 不够就将数组长度设置为需要的长度
        // 只有第一次执行的时候才会进入
        if (newCapacity - minCapacity < 0)
            // 如果第一次执行,newCapacity = 0  minCapacity = 10 
            // 初始容量在此定义为 10
            newCapacity = minCapacity;
        //判断有没超过最大限制
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        // 将原来数组的值copy新数组中去, ArrayList的引用指向新数组
        // 这儿会新创建数组,如果数据量很大,重复的创建的数组,那么还是会影响效率,
        // 因此鼓励在合适的时候通过构造方法指定默认的capaticy大小
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
    
    public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }
    
    
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        // 判断 elementData 是否为空数组
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 只有第一次调用add方法才会进入
            // DEFAULT_CAPACITY = 10
            // minCapacity = 1
            // 调用max方法,返回 10
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
    // a = 10 , b = 1 
    public static int max(int a, int b) {
        return (a >= b) ? a : b;
    }

}

源码remove方法

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

总结

  • arrayList 在JDK1.6之前默认长度10,1.6之后默认为 0
  • 当用户第一次调用add方法时,arrayList会进行第一次扩容,扩容后大小为 10
  • arrayList在添加第11个元素的时候,会进行第二次扩容
  • 迭代数组的时候,如果删除数组元素,后面数组元素就会前移一个位置,所以就会引起并发修改异常,解决:
    • 不使用迭代器,for循环即可
    • CopyOnWriteArrayList 可读写的ArrayList
发布了34 篇原创文章 · 获赞 14 · 访问量 1568

猜你喜欢

转载自blog.csdn.net/Wan_Yuan/article/details/105232123