ArrayList源码浅析

ArrayList这个类,对于Android开发者来说太简单不过了:它是一个集合类,用来存放元素。比如说经常的用法是这样:List list = new ArrayList();我们可以对list集合进行一些基本的操作——增删改查。当然这些只是停留于会用ArrayList,你是否了解ArrayList内部实现原理?你是否了解其内部增删改查机制?你是否对它的扩容机制了然于心?如果你知道的话,下面这些内容你可以略过了;不知道的话,我们继续开车。

一、ArrayList源码解析

打开Android SDK的ArrayList源码,我们首先从其构造函数入手:
1、构造函数
从源码中我们可以看到ArrayList有三个构造函数,分别如下:
a).无参构造函数

/**
     * Constructs a new {@code ArrayList} instance with zero              initial capacity.
     */
    public ArrayList() {
        array = EmptyArray.OBJECT;
    }

其中,array是ArrayList的成员变量,为transient Object[] array;为一个数组
,所以我们知道ArrayList内部是由一个数组来实现的。
b).形参为一个整形的构造函数

/**
     * Constructs a new instance of {@code ArrayList} with the specified
     * initial capacity.
     *
     * @param capacity
     *            the initial capacity of this {@code ArrayList}.
     */
    public ArrayList(int capacity) {
        if (capacity < 0) {
            throw new IllegalArgumentException("capacity < 0: " + capacity);
        }
        array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
    }

首先判断capacity是否小于0,如果小于0,则抛出IllegalArgumentException异常;如果等于0,则创建一个跟无参构造函数一样的数组;如果大于0,则创建大小为capacity数组。
c).形参为一个集合的构造函数

/**
     * Constructs a new instance of {@code ArrayList} containing the elements of
     * the specified collection.
     *
     * @param collection
     *            the collection of elements to add.
     */
    public ArrayList(Collection<? extends E> collection) {
        if (collection == null) {
            throw new NullPointerException("collection == null");
        }

        Object[] a = collection.toArray();
        if (a.getClass() != Object[].class) {
            Object[] newArray = new Object[a.length];
            System.arraycopy(a, 0, newArray, 0, a.length);
            a = newArray;
        }
        array = a;
        size = a.length;
    }

首先判断collection是否为空,为空的话则抛出NullPointerException异常;反之,则将collection转换成一个数组赋值给局部数组a,然后判断a是否是Object数组类型,如Object数组类型,最后将a赋值给ArrayList的临时变量array,另外成员变量size的值等于数组a的长度。

接下来我们看下ArrayList是如何添加元素的:
2、ArrayList的add方法
ArrayList的add方法也有四种重载方式,我们分别来看下:
a)、形参为一个参数的(参数是一个Object)

/**
     * Adds the specified object at the end of this {@code ArrayList}.
     *
     * @param object
     *            the object to add.
     * @return always true
     */
    @Override public boolean add(E object) {
        Object[] a = array;
        int s = size;
        if (s == a.length) {
            Object[] newArray = new Object[s +
                    (s < (MIN_CAPACITY_INCREMENT / 2) ?
                     MIN_CAPACITY_INCREMENT : s >> 1)];
            System.arraycopy(a, 0, newArray, 0, s);
            array = a = newArray;
        }
        a[s] = object;
        size = s + 1;
        modCount++;
        return true;
    }

首先将array赋值给局部a数组,size赋值给局部变量a,然后判断s是否等于a数组的长度,其实这里一般是相等的,不知道老外什么逻辑,进入if判断之后,创建一个newArray数组,其容量值通过一个三元表达式来判断,先判断是s的值是否小于MIN_CAPACITY_INCREMENT / 2,其中MIN_CAPACITY_INCREMENT默认的值为12,也即是判断s是否小于6,如果s的值小于6则新数组的大小为s+12,反之则新数组的大小为s+(s大小的一半).然后将a的元素赋值到newArray中,并且修改array及a的值。最后将object放到a[s]位置、size加1、modCout自增1,其中modCount是记录修改次数的。

b)形参为两个参数的

/**
     * Inserts the specified object into this {@code ArrayList} at the specified
     * location. The object is inserted before any previous element at the
     * specified location. If the location is equal to the size of this
     * {@code ArrayList}, the object is added at the end.
     *
     * @param index
     *            the index at which to insert the object.
     * @param object
     *            the object to add.
     * @throws IndexOutOfBoundsException
     *             when {@code location < 0 || location > size()}
     */
    @Override public void add(int index, E object) {
        Object[] a = array;
        int s = size;
        if (index > s || index < 0) {
            throwIndexOutOfBoundsException(index, s);
        }

        if (s < a.length) {
            System.arraycopy(a, index, a, index + 1, s - index);
        } else {
            // assert s == a.length;
            Object[] newArray = new Object[newCapacity(s)];
            System.arraycopy(a, 0, newArray, 0, index);
            System.arraycopy(a, index, newArray, index + 1, s - index);
            array = a = newArray;
        }
        a[index] = object;
        size = s + 1;
        modCount++;
    }

这个add方法的意思是说在index位置插入object值,也即是在指定位置插入某个值。首先将array赋值给a,size赋值给s,然后判断index是否越界,是的话抛出越界异常;反之往下走,如果s小于a的长度,则将a数组的index开始到s-index位置的元素赋值到a的index开始后的元素;如果s大于a的长度则创建一个newArray数组,其大小也是扩容机制后的大小,实现跟上面的一样,这里不再详述。然后将数组a中0到index位置的元素copy到newArrayList中,再把数组a中index到s-index位置的元素copy到newArrayList中,newArray赋值给array及a,最后将object放到a[s]位置上,size加1,modCount自增1。
c)、形参为一个参数的(是一个集合)

 /**
     * This method controls the growth of ArrayList capacities.  It represents
     * a time-space tradeoff: we don't want to grow lists too frequently
     * (which wastes time and fragments storage), but we don't want to waste
     * too much space in unused excess capacity.
     *
     * NOTE: This method is inlined into {@link #add(Object)} for performance.
     * If you change the method, change it there too!
     */
    private static int newCapacity(int currentCapacity) {
        int increment = (currentCapacity < (MIN_CAPACITY_INCREMENT / 2) ?
                MIN_CAPACITY_INCREMENT : currentCapacity >> 1);
        return currentCapacity + increment;
    }

    /**
     * Adds the objects in the specified collection to this {@code ArrayList}.
     *
     * @param collection
     *            the collection of objects.
     * @return {@code true} if this {@code ArrayList} is modified, {@code false}
     *         otherwise.
     */
    @Override public boolean addAll(Collection<? extends E> collection) {
        Object[] newPart = collection.toArray();
        int newPartSize = newPart.length;
        if (newPartSize == 0) {
            return false;
        }
        Object[] a = array;
        int s = size;
        int newSize = s + newPartSize; // If add overflows, arraycopy will fail
        if (newSize > a.length) {
            int newCapacity = newCapacity(newSize - 1);  // ~33% growth room
            Object[] newArray = new Object[newCapacity];
            System.arraycopy(a, 0, newArray, 0, s);
            array = a = newArray;
        }
        System.arraycopy(newPart, 0, a, s, newPartSize);
        size = newSize;
        modCount++;
        return true;
    }

首先是将collection转成数组放在newPart中,newPartSize代表newPart数组的大小,然后将array赋值给局部数组a,size赋值给s,新数组的size等于s+newPartSize,然后判断新数组是否大于a.length,如果大于的话则根据新数组的size进行扩容,将a中的元素copy到newArray中,并且newArray赋值给array及a.再将newPart中的元素copy到a的s到newPartSize位置,size等于newSize,modCount自增1。

d)、在指定位置添加集合

扫描二维码关注公众号,回复: 175112 查看本文章
/**
     * Inserts the objects in the specified collection at the specified location
     * in this List. The objects are added in the order they are returned from
     * the collection's iterator.
     *
     * @param index
     *            the index at which to insert.
     * @param collection
     *            the collection of objects.
     * @return {@code true} if this {@code ArrayList} is modified, {@code false}
     *         otherwise.
     * @throws IndexOutOfBoundsException
     *             when {@code location < 0 || location > size()}
     */
    @Override
    public boolean addAll(int index, Collection<? extends E> collection) {
        int s = size;
        if (index > s || index < 0) {
            throwIndexOutOfBoundsException(index, s);
        }
        Object[] newPart = collection.toArray();
        int newPartSize = newPart.length;
        if (newPartSize == 0) {
            return false;
        }
        Object[] a = array;
        int newSize = s + newPartSize; // If add overflows, arraycopy will fail
        if (newSize <= a.length) {
             System.arraycopy(a, index, a, index + newPartSize, s - index);
        } else {
            int newCapacity = newCapacity(newSize - 1);  // ~33% growth room
            Object[] newArray = new Object[newCapacity];
            System.arraycopy(a, 0, newArray, 0, index);
            System.arraycopy(a, index, newArray, index + newPartSize, s-index);
            array = a = newArray;
        }
        System.arraycopy(newPart, 0, a, index, newPartSize);
        size = newSize;
        modCount++;
        return true;
    }

首先将size赋值给s,如果index越界,则抛throwIndexOutOfBoundsException异常。把collection转成数组放在newPart中,newPartSize代表newPart数组的大小,然后将array赋值给a,newSize等于s+newPart的长度,判断newSize是否小于a的长度,如果小于的话则直接a中index开始的元素放到index+newPartSize开始的位置;反之往下走,根据newSize进行扩容,创建newArray,再将a中0到index的元素copy到newArray,a中index后的所有元素copy到newArray中的index+newPartSize到s-index位置,最后再将newArray赋值给array及a,将newPart中的元素copy到a的index到newPartSize位置,size等于newSize,modCount自增1。

接下来我们来看下ArrayList是如何移除元素的:
3、ArrayList的remove方法
a)、形参为一个整形下标

/**
     * Removes the object at the specified location from this list.
     *
     * @param index
     *            the index of the object to remove.
     * @return the removed object.
     * @throws IndexOutOfBoundsException
     *             when {@code location < 0 || location >= size()}
     */
    @Override public E remove(int index) {
        Object[] a = array;
        int s = size;
        if (index >= s) {
            throwIndexOutOfBoundsException(index, s);
        }
        @SuppressWarnings("unchecked") E result = (E) a[index];
        System.arraycopy(a, index + 1, a, index, --s - index);
        a[s] = null;  // Prevent memory leak
        size = s;
        modCount++;
        return result;
    }

首先将array赋值给局部数组a,size赋值给s,假如index大于等于s,则是越界异常。然后将index开始的位置元素往前移一个位置,a[s]置空,size减1。modCount自增1。返回删除元素。
b)、形参为一个object

@Override public boolean remove(Object object) {
        Object[] a = array;
        int s = size;
        if (object != null) {
            for (int i = 0; i < s; i++) {
                if (object.equals(a[i])) {
                    System.arraycopy(a, i + 1, a, i, --s - i);
                    a[s] = null;  // Prevent memory leak
                    size = s;
                    modCount++;
                    return true;
                }
            }
        } else {
            for (int i = 0; i < s; i++) {
                if (a[i] == null) {
                    System.arraycopy(a, i + 1, a, i, --s - i);
                    a[s] = null;  // Prevent memory leak
                    size = s;
                    modCount++;
                    return true;
                }
            }
        }
        return false;
    }

首先将array赋值给局部数组a,size赋值给s,然后判断object是否为空,不为空的话则遍历a,当a[i]等于object的时候,将i位置之后的元素往前移一个位置,再将a[s]置空,size减1,modCount自增1,接着继续循环,直到遍历完为止。如果object为空的话,原理跟前面一样,这里不再讲述。

4、ArrayList的removeRange方法

@Override protected void removeRange(int fromIndex, int toIndex) {
        if (fromIndex == toIndex) {
            return;
        }
        Object[] a = array;
        int s = size;
        if (fromIndex >= s) {
            throw new IndexOutOfBoundsException("fromIndex " + fromIndex
                    + " >= size " + size);
        }
        if (toIndex > s) {
            throw new IndexOutOfBoundsException("toIndex " + toIndex
                    + " > size " + size);
        }
        if (fromIndex > toIndex) {
            throw new IndexOutOfBoundsException("fromIndex " + fromIndex
                    + " > toIndex " + toIndex);
        }

        System.arraycopy(a, toIndex, a, fromIndex, s - toIndex);
        int rangeSize = toIndex - fromIndex;
        Arrays.fill(a, s - rangeSize, s, null);
        size = s - rangeSize;
        modCount++;
    }

首先判断fromIndex是否等于toIndex,如果等于直接返回。往下走,把array赋值给a,size赋值给s,如果fromIndex或toIndex大于s、fromIndex大于toIndex则抛出异常,然后将a中toIndex开始的元素往前移动rangeSize个位置并且a中s-rangeSize开始到s位置的元素置空,size变为s-rangeSize,modCount自增1。

5、ArrayList的set方法

/**
     * Replaces the element at the specified location in this {@code ArrayList}
     * with the specified object.
     *
     * @param index
     *            the index at which to put the specified object.
     * @param object
     *            the object to add.
     * @return the previous element at the index.
     * @throws IndexOutOfBoundsException
     *             when {@code location < 0 || location >= size()}
     */
    @Override public E set(int index, E object) {
        Object[] a = array;
        if (index >= size) {
            throwIndexOutOfBoundsException(index, size);
        }
        @SuppressWarnings("unchecked") E result = (E) a[index];
        a[index] = object;
        return result;
    }

修改指定位置的元素,首先将array赋值给a,如果index大于size则抛出越界异常,result记录修改前位置元素值,然后才将该位置元素的值置为object,把result返回。

6、ArrayList的clear的方法

/**
     * Removes all elements from this {@code ArrayList}, leaving it empty.
     *
     * @see #isEmpty
     * @see #size
     */
    @Override public void clear() {
        if (size != 0) {
            Arrays.fill(array, 0, size, null);
            size = 0;
            modCount++;
        }
    }

如果size不等于0,则用null填充各个位置的元素,size置为0,modCount自增1。

好了,ArrayList的源码分析就到这为止了,技术水平有限,不正之处欢迎批评指正。谢谢各位。

猜你喜欢

转载自blog.csdn.net/wufeiqing/article/details/78025290