我要剖析你之ArrayList源码分析

  • 预备知识
  • ArrayList
    • 继承关系
    • 实现原理
    • 源码分析
  • 总结

预备知识

1、了解方法System.arraycopy(src, srcPos, dest, destPos, length)。这是一个本地方法,方法的主要作用是将src数组中从srcPos位置开始的元素复制到目标数组dest中,并从dest的destPost位置开始填充。length为需要复制src的中元素的个数。
2、位运算“>>”的作用:xx>>1相当于xx/2
3、另外需要说明的是次分析是基于jdk1.7.0_79

ArrayList

继承关系

我要剖析你之ArrayList源码分析_第1张图片

*需要说明的是这里只分析ArrayList,所以AbstractList的继承体系并没有详细画出

实现原理

在ArrayList的实现原理其实很简单:在内部使用一个Object数组存放数据,当需要储存数据的数据量大于这个数组的长度时,则重新创建一个数组,将原数组的数据复制到这个新数组中,这样就实现了自动扩容。

源码分析

为了在分析的时候,减少代码注释和方法跳转说明,先来看几个比较重要的成员变量和私有方法,熟悉完成下面的属性和私有方法,ArrayList的核心源码就差不多已经了解了。

成员属性

//定义在ArstractList中,每增加删除一个元素都会+1
protected transient int modCount = 0;
//默认初始容量
private static final int DEFAULT_CAPACITY = 10;
//长度为0的数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//用于存放元素的数组
private transient Object[] elementData;
//记录元素个数
private int size;
//最大允许保存元素数量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

私有方法

   //核心方法:从方法名可以看这,这个是保证elementData是否有足够容量来存放元素的方法
   private void ensureCapacityInternal(int minCapacity) {
        //如果elementData还是0长度数组
        if (elementData == EMPTY_ELEMENTDATA) {
            //DEFAULT_CAPACITY默认为10的
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

    //核心方法:这里开始计算,判断是否需要扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // 如果minCapacity>elementData.length,需要扩容了
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    //核心方法:自动扩容的实现
    private void grow(int minCapacity) {
        //保存旧容量
        int oldCapacity = elementData.length;
        //计算出新容量,即在旧容量的基础上扩容50%
        int newCapacity = oldCapacity + (oldCapacity >> 1);

        //这里是对newCapacity 进行修正,避免newCapacity>MAX_ARRAY_SIZE或<旧容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 这里是将elementData复制到一个新数组中,新数组的长度为之前计算出来的newCapacity
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    //这里检查index有超出元素的个数
    private void rangeCheck(int index) {
        //size记录了elementData中保存的元素数量,并不一定等于elementData.length。
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    //与rangeCheck相似,只是增加了判断index<0
    private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

现在从构造方法开始,逐步分析在日常生活中常用的方法。

public ArrayList() {
    super();
    //默认的构造函数是不会创建新的Object数组的
    this.elementData = EMPTY_ELEMENTDATA;
}

public ArrayList(int initialCapacity) {
     super();
     if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    //创建一个Object数组
    this.elementData = new Object[initialCapacity];
}

public ArrayList(Collection c) {
    elementData = c.toArray();
    size = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    if (elementData.getClass() != Object[].class)
         //这个方法底层也是调用的System.arraycopy(src,  srcPos, dest, destPos, length)这个方法
         elementData = Arrays.copyOf(elementData, size, Object[].class);
}

//添加一个元素,核心在ensureCapacityInternal方法中
public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

//添加元素到指定位置,核心点在如何空出index这个位置
public void add(int index, E element) {
    rangeCheckForAdd(index);//边界检查
    //自动扩容判断,保证elementData有足够的容量来存放元素
    ensureCapacityInternal(size + 1);  
    //这里实现了将elementData中的元素,将index及其之后的元素全部向后移一位,这样就将index位置空出
    System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
    //将e插入到index位置
    elementData[index] = element;
    size++;
}

//添加一个集合
public boolean addAll(Collection c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    //保证element有足够的空间
    ensureCapacityInternal(size + numNew);
    //复制数据到elementData中
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}
//将一个集合添加到指定的位置,其实现方式与add(index, e)类似,都是通过先挪出足够的空间,再将数据复制到其中
public boolean addAll(int index, Collection c) {
    rangeCheckForAdd(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    //保证element有足够的空间
    ensureCapacityInternal(size + numNew);

    int 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;
}
//删除指定位置的元素
public E remove(int index) {
    //范围检查,超过范围会抛出一异常
    rangeCheck(index);

    modCount++;
    E oldValue = elementData(index);

    //重新整理elementData,即将index后面的元素全部往前移一个位置,与add刚好相反
    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;
}

//删除指定元素
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;
}

//删除指定元素的最终实现,其实与remove实现几乎一样,少了一个范围检查而已
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
}

//删除指定范围内的元素,实现方式就是拿toIndex之后的元素,覆盖掉fromIndex之后的元素
protected void removeRange(int fromIndex, int toIndex) {
    modCount++;
    int numMoved = size - toIndex;
    //将toIndex之后的元素前移toIndex-fromIndex位
    System.arraycopy(elementData, toIndex, elementData, fromIndex,
             numMoved);

    // 计算出新的元素个数,将清除超过newSize的元素,因为它们是已经删除的
    int newSize = size - (toIndex-fromIndex);
    for (int i = newSize; i < size; i++) {
        elementData[i] = null;
    }

    //更新size值
    size = newSize;
}

//查找元素,没有什么可分析的,逐个比较元素而已。
public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

//清楚所有元素
public void clear() {
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;
}

现在常用方法已经分析完成,最后在看看Itertaor是怎么实现的

private class Itr implements Iterator<E> {
    int cursor;       //当前访问元素的坐标
    int lastRet = -1; //上一次返回元素的坐标
    int expectedModCount = modCount;//亮点来了:将ArrayList被修改的次数保存下来,想要知道有什么用,继续看~_~

    //为了看两点,我把这段代码位置提前了
    final void checkForComodification() {
        //快看、快看,亮点出来了:这里判断了“当前的modCount”与“创建Iterator对象时的modCount”是否一样,如果不一样,则会抛出ConcurrentModificationException异常。看到这里各位看官是否想到ArrayList不支持多线程修改的问题了~_~。其实抛出ConcurrentModificationException不仅仅可以发生在多个线程同时修改的时候,及时在单线程也可能发生,具体怎么发生各位肯定已经想到了。
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

    //只要当前范围元素的坐标不等于size,那么就还有元素
    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        //检查ArrayList有没有被修改过
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();

        Object[] elementData = ArrayList.this.elementData;
        //重复检查ArrayList有没有被修改过
        if (i >= elementData.length)
            throw new ConcurrentModificationException();

        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();

        //检查ArrayList有没有被修改过
        checkForComodification();

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            //内部重新获取最新的modCount值
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}

另外ArrayList内部还有一个SubList,不过了解上面实现之后,SubList也简单了,想要了解各位可以自己看看,这里就不在描述。

总结

其实ArrayList的实现相对比较简单,内部也就一个Object[]存放数据。不像HashMap那么相对复杂。只需要理解了它的自动扩容机制,ArrayList的源码就简单了。

你可能感兴趣的:(源码分析)