为了账号安全,请及时绑定邮箱和手机立即绑定

源码阅读-ArrayList

标签:
Android

以下源码基于 Android SDK 23, 与JDK中略有差别,但基本相同;整体源码由 构造、添加(add)、设置(set)、获取(get)、移除(remove)、迭代器(iterator) 和序列化(Serializable)组成,最后我还会把里边一些不常用的方法举例说明下作用,下面我们就一一探究其实现原理。

概述

ArrayList 存储的实质是操作一个数组,这个数组可以根据内容自动扩容,所以让 ArrayList看起来像一个无限大小的容器一样。

属性
/**
     * The minimum amount by which the capacity of an ArrayList will increase.
     * This tuning parameter controls a time-space tradeoff. This value (12)
     * gives empirically good results and is arguably consistent with the
     * RI's specified default initial capacity of 10: instead of 10, we start
     * with 0 (sans allocation) and jump to 12.
     */
    private static final int MIN_CAPACITY_INCREMENT = 12;    /**
     * The number of elements in this list.
     */
    int size;    /**
     * The elements in this list, followed by nulls.
     */
    transient Object[] array;
  • 既然ArrayList可以自动扩容,那么就要有一个描述每次扩容的基准,MIN_CAPACITY_INCREMENT就是这个基准,默认值是12。

  • array 是 ArrayList的核心,所有数据均存储在array这个数组中,发生自动扩容时, array 会指向新的数组首地址,但注意了,transient  表示它不会参与序列化过程。

  • size 始终描述 ArrayList 中实际的大小。

构造方法
/**
     * 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]);
    }    /**
     * Constructs a new {@code ArrayList} instance with zero initial capacity.
     */
    public ArrayList() {
        array = EmptyArray.OBJECT;
    }    /**
     * 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;
    }

ArrayList共含有3个构造方法,EmptyArray.OBJECT是一个length为0的空数组new Object[0],new ArrayList() 则会创建一个大小为0的数组;你也可以去指定初始的容量capacity,new ArrayList(int capacity) ,避免ArrayList 第一次add 或者其他操作就进行扩容;第三个构造可以传入一个集合,这里要提一下Collection,你可以认为它是 List 、QueueSet的始祖,这里只要在它们内部实现了 toArray 方法,并且返回一个Object[]类型的数据,就可以成功初始化到 ArrayList中。

添加(add / addAll)
/**
     * 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;
    }    /**
     * 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++;
    }    /**
     * 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;
    }    /**
     * 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;
    }

这里有必要先看一个方法,System.arraycopy()

public static native void arraycopy(Object src, int srcPos,
        Object dst, int dstPos, int length);

这是一个 native方法,负责数组拷贝,从 src 的 srcPos 开始,将 length长度的数据拷贝到 dst 中,dstPos中的数据是srcPos位置的数据。

public boolean add(E object)

这是最简单的一个add操作,里边会进行扩容判断,如果当前ArrayList.sizearray.length相同,则进行扩容,扩容的策略是`s < (MIN_CAPACITY_INCREMENT / 2) ?

                 MIN_CAPACITY_INCREMENT : s >> 1`,即 s < 6 ? 6 : s * 2, 最终扩容的大小为  (s + s < 6 ? 6 : s * 2);`newCapacity(int currentCapacity) ` 方法也是这个作用,返回最终扩容后的大小。
public void add(int index, E object)

这个方法的作用是将 object 插入至 index位置,这里也会有扩容判断,既然是插入一个值,那么size就会 +1,所以 ArrayList.size 小于 array.length是一种情况,数组可以直接从 index处 后移一位,再将 object 放入 index的位置;若是大于等于,则原array需要扩容,扩容后现将old array 数据 复制到 new array中,再进行后移,最终把object插入到index位置。

public boolean addAll(Collection<? extends E> collection)public boolean addAll(int index, Collection<? extends E> collection)

这两个方法只是批量操作,内部逻辑与add 是一样的,都要先判断 ArrayList.sizearray.length的大小关系进行扩容,之后通过 System.arraycopy去操作array

注:这里你有可能会发现有个变量 modCount,它用来表达ArrayList的修改次数(add、remove),是它导致ArrayList不是线程安全的,等讲到迭代器iterator的时候再来说说这个变量。

设置
/**
     * 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[index] 替换,并且把原来的数据返回。

获取
@SuppressWarnings("unchecked") 
@Override public E get(int index) {        if (index >= size) {
            throwIndexOutOfBoundsException(index, size);
        }        return (E) array[index];
    }

这个方法也不多说,将array[index]返回。

移除
/**
     * 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;
    }    @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;
    }    @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++;
    }

add方法已经进行了详细的讲解,想必大家都能猜到,remove操作就是讲 index 或者 range的一段数据从array中移除,然后再通过System.arraycopy拷贝之后的数据前移补充空位,下图以移除单个为例,将步骤分解:
image.png

迭代器
@Override public Iterator<E> iterator() {        return new ArrayListIterator();
    }    private class ArrayListIterator implements Iterator<E> {        /** Number of elements remaining in this iteration */
        private int remaining = size;        /** Index of element that remove() would remove, or -1 if no such elt */
        private int removalIndex = -1;        /** The expected modCount value */
        private int expectedModCount = modCount;        public boolean hasNext() {            return remaining != 0;
        }        @SuppressWarnings("unchecked") public E next() {
            ArrayList<E> ourList = ArrayList.this;            int rem = remaining;            if (ourList.modCount != expectedModCount) {                throw new ConcurrentModificationException();
            }            if (rem == 0) {                throw new NoSuchElementException();
            }
            remaining = rem - 1;            return (E) ourList.array[removalIndex = ourList.size - rem];
        }        public void remove() {
            Object[] a = array;            int removalIdx = removalIndex;            if (modCount != expectedModCount) {                throw new ConcurrentModificationException();
            }            if (removalIdx < 0) {                throw new IllegalStateException();
            }
            System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining);
            a[--size] = null;  // Prevent memory leak
            removalIndex = -1;
            expectedModCount = ++modCount;
        }
    }

迭代器,一个很重要的概念,它的作用就是便利整个ArrayList, for each 的原理其实就是迭代器的使用,上文说到了modCount与迭代器相关,

if (ourList.modCount != expectedModCount) {          throw new ConcurrentModificationException();
}

expectedModCountiterator初始化时赋予的值,值为modCount,而modCount会根据add或者remove进行++操作,这就表明,当iterator创建好后,只要使用这个iterator实例去进行遍历,就不能使用ArrayList.add或者ArrayList.remove操作,因为如果使用了,modCount会发生变化,这样在next()的时候就会抛出异常ConcurrentModificationException ,这也进一步说明ArrayList不是线程安全的。那么在遍历中如何移除元素呢,就是下边实现的remove方法了,remove过程与之前类似,关键在于expectedModCount = ++modCount;remove需要使modCount 递增,那么我让expectedModCount重新赋值,即可完成删除操作。

序列化
private static final long serialVersionUID = 8683452581122892189L;    private void writeObject(ObjectOutputStream stream) throws IOException {
        stream.defaultWriteObject();
        stream.writeInt(array.length);        for (int i = 0; i < size; i++) {
            stream.writeObject(array[i]);
        }
    }    private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException {
        stream.defaultReadObject();        int cap = stream.readInt();        if (cap < size) {            throw new InvalidObjectException(                    "Capacity: " + cap + " < size: " + size);
        }
        array = (cap == 0 ? EmptyArray.OBJECT : new Object[cap]);        for (int i = 0; i < size; i++) {
            array[i] = stream.readObject();
        }
    }

这是在代码末尾了,ArrayList是通过stream.writeObject 连续写入 array的内容。

其他
 public boolean contains(Object object)

利用 object 的 equals方法判断ArrayList中是否包含object对象。

 public int indexOf(Object object)

 public int lastIndexOf(Object object)

这两个方法都是获取 object 在 ArrayList中的位置,第一个是正序遍历,找到的第一个返回的index;第二个是倒序遍历,找到第一个返回的index

 /**
     * Sets the capacity of this {@code ArrayList} to be the same as the current
     * size.
     *
     * @see #size
     */
    public void trimToSize() {        int s = size;        if (s == array.length) {            return;
        }        if (s == 0) {
            array = EmptyArray.OBJECT;
        } else {
            Object[] newArray = new Object[s];
            System.arraycopy(array, 0, newArray, 0, s);
            array = newArray;
        }
        modCount++;
    }

这个方法是将当前的array “精简”一下,比如 array.length 是10,但里边的size是 5个,那么就将 array.length变为 5,把数据通过 System.arraycopy 拷贝到新的 array中。

 @Override public boolean equals(Object o) {        if (o == this) {            return true;
        }        if (!(o instanceof List)) {            return false;
        }
        List<?> that = (List<?>) o;        int s = size;        if (that.size() != s) {            return false;
        }
        Object[] a = array;        if (that instanceof RandomAccess) {            for (int i = 0; i < s; i++) {
                Object eThis = a[i];
                Object ethat = that.get(i);                if (eThis == null ? ethat != null : !eThis.equals(ethat)) {                    return false;
                }
            }
        } else {  // Argument list is not random access; use its iterator
            Iterator<?> it = that.iterator();            for (int i = 0; i < s; i++) {
                Object eThis = a[i];
                Object eThat = it.next();                if (eThis == null ? eThat != null : !eThis.equals(eThat)) {                    return false;
                }
            }
        }        return true;
    }

再来看下这个长长的equals方法,非常好懂,但是乍眼一看有个 RandomAccess ,这是什么?寻找了一下它的实现类,发现ArrayList就是它的实现类,再看下这个if(...){}else{},如果是RandomAccess的实现类,那么直接使用get(index)获取元素,否则需要使用迭代器iterator。以下是对于RandomAccess的一段摘录:

jdk中有个RandomAccess接口,这是一个标记接口(Marker),它没有任何方法,这个接口被List的实现类(子类)使用。如果List子类实现了RandomAccess接口,那就表示它能够快速随机访问存储的元素。RandomAccess接口的意义在于:在对列表进行随机或顺序访问的时候,访问算法能够选择性能最佳方式。一般的列表访问算法在访问列表元素之前,都被建议先使用instanceof关键字检查一下列表是否是一个RandomAccess子类,然后再决定采用随机还是顺序方式访问列表中的元素,这样可以保证访问算法拥有最佳的性能。对于List的子类,如果:

for (int i=0, n=list.size(); i < n; i++)

list.get(i);

的访问方式比
for (Iterator i=list.iterator(); i.hasNext(); )

i.next();

快,那么它应该实现RandomAccess接口。

原文链接:http://www.apkbus.com/blog-487165-76768.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消