集合源码学习(三):ArrayList

来源:互联网 发布:恺英网络借壳泰亚股份 编辑:程序博客网 时间:2024/06/07 02:23

Java集合里面最简单的估计就是ArrayList了,当然在我刚接触Java时,甚至接触相当一段时间,我都不知道ArrayList原理的。这两天把ArrayList源码仔细读了一遍,感觉如果把注释完的代码贴出来还不如直接看jdk文档。我把一些核心的地方找出来,下面将从以下几个方面来学习ArrayList

什么是ArrayList?

其实ArrayList里面的数据结构知识就是线性表的应用。

关于modCount

modCount是ArrayList继承自AbstractArrayList的一个字段。ArrayList有个fast-fail机制。fail-fast产生的原因就在于程序在对 Collection 进行迭代(Iterator操作)时,某个线程对该 Collection 在结构上对其做了修改(add和delete操作),这时迭代器就会抛出 ConcurrentModificationException 异常信息,从而产生 fail-fast。
jdk注释为:

The iterators returned by this class's iterator and listIterator methods are fail-fast:  if the vector is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throwa ConcurrentModificationException. Thus, in the face of concurrent modification, the iteratorfails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future. The Enumerations returned by the elements method are not fail-fast.Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking,impossible to make any hard guarantees in the presence of unsynchronized concurrent modification.Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it wouldbe wrong to write a program that depended on this exception for its correctness: the fail-fastbehavior of iterators should be used only to detect bugs.

定义

public class ArrayList<E> extends AbstractList<E>        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

从定义头的代码可以看出,ArrayList继承自AbstractList,支持泛型,可以随机存取,可以克隆,可以序列化都在代码中有所体现。

是否可为null?

可以

长度以及自动增长的长度

默认情况下,ArrayList内置数组长度为10,

/**     * 如果不指定arraylist大小,java8里面默认大小是10.     */    private static final int DEFAULT_CAPACITY = 10;

如果范围不够了,自动增长长度将为原长度的1.5倍

  /**     * 因为是按照老数组长度大小的1.5倍去调的,所以如果给定的minCapacity没有其1.5倍这么大,     * 还是会调到老数组长度1.5倍。     */    private void grow(int minCapacity) {        // overflow-conscious code        int oldCapacity = elementData.length;        int newCapacity = oldCapacity + (oldCapacity >> 1);         //老数组大小的1.5倍。        if (newCapacity - minCapacity < 0)            newCapacity = minCapacity;        if (newCapacity - MAX_ARRAY_SIZE > 0)            newCapacity = hugeCapacity(minCapacity);        // minCapacity is usually close to size, so this is a win:        elementData = Arrays.copyOf(elementData, newCapacity);    }

当然如果主动要求扩展大于原长1.5倍则取指定大小

ArrayList的最大长度

在源码中,有MAX_ARRAY_SIZE 指定ArrayList最大长度为:

  /**     * array的最大长度,减去8的原因是,有些vm的header words可能会占据一些空间。     */    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

contains(Object o)的实现原理

实际上,在ArrayList中,需要判断o是否为null,如果为null,则是用= 判断相等,而不为null时,则是使用equals判断。而整个判断的流程就是遍历所有元素,如果存在就返回true,否则返回false。

    /**     *判断是否含有某个元素。     */    public boolean contains(Object o) {        return indexOf(o) >= 0;    }    /**     * 判断的根据是遍历一遍,并且是通过equals遍历的。注意equals的用法与注意事项。     * 包括null。     */    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;    }

clone方法

ArrayList实现clone方法其实比较简单,这里将clone方法提出来是觉得一个细节处理的很好,就是v.modCount=0 这句代码。如果没有这句代码,把本类的modCount也克隆过去可就不好了。

 /**     * clone方法,注意此时还把v的modCount属性设为0了,防止由于本身是1导致结果异常。     */    public Object clone() {        try {            ArrayList<?> v = (ArrayList<?>) super.clone();            v.elementData = Arrays.copyOf(elementData, size);            v.modCount = 0;            return v;        } catch (CloneNotSupportedException e) {            // this shouldn't happen, since we are Cloneable            throw new InternalError(e);        }    }

set,get,add,remove等方法

set方法比较简单,随机访问数组,并且把该下标值设为相应值。
rangeCheck为越界检查的方法。

/**     * 设置泛型的类型     */    public E set(int index, E element) {        rangeCheck(index);        E oldValue = elementData(index);        elementData[index] = element;        return oldValue;    }

get方法:

 /**     * 得到泛型的类型     */    public E get(int index) {        rangeCheck(index);        return elementData(index);    }

普通add:在末尾添加就可以了。

 /**     * 增加泛型的类型,在末尾增加     */    public boolean add(E e) {        ensureCapacityInternal(size + 1);  // Increments modCount!!        elementData[size++] = e;        return true;    }

有指定位置的add和delete:这样的话,就需要调整一遍数组,如果在中间插入(add),就需要把其后面的往后挪一个位置。另外如果在中间删除,就需要把后面的都往前面挪一个位置。不要问为什么要挪,这是基础。

 /**     * 将特定的元素插入到特定的位置。     * 把后面的元素往后统一挪一个。     */    public void add(int index, E element) {        rangeCheckForAdd(index);        ensureCapacityInternal(size + 1);  // Increments modCount!!        //挪一个操作        System.arraycopy(elementData, index, elementData, index + 1,                         size - index);        elementData[index] = element;        size++;    }

当然还有其他的一些方法例如
protected void removeRange(int fromIndex, int toIndex)
public boolean addAll(Collection<? extends E> c)
public boolean remove(Object o)


其他的增加,删除,修改等操作和上面讲的原理差不多,这里不赘述。

其他方法

另外在ArrayList还支持了对流操作:

 /**     * 写入流     */    private void writeObject(java.io.ObjectOutputStream s)        throws java.io.IOException{        // Write out element count, and any hidden stuff        int expectedModCount = modCount;        s.defaultWriteObject();        // Write out size as capacity for behavioural compatibility with clone()        s.writeInt(size);        // Write out all elements in the proper order.        for (int i=0; i<size; i++) {            s.writeObject(elementData[i]);        }        //这样的方法严格控制单个线程        if (modCount != expectedModCount) {            throw new ConcurrentModificationException();        }    }    /**     * 读入流里面的数据     */    private void readObject(java.io.ObjectInputStream s)        throws java.io.IOException, ClassNotFoundException {        elementData = EMPTY_ELEMENTDATA;        // Read in size, and any hidden stuff        s.defaultReadObject();        // Read in capacity        s.readInt(); // ignored        if (size > 0) {            // be like clone(), allocate array based upon size not capacity            ensureCapacityInternal(size);            Object[] a = elementData;            // Read in all elements in the proper order.            for (int i=0; i<size; i++) {                a[i] = s.readObject();            }        }    }

既然是可以随机访问的集合,所以ArrayList里面还自己实现了可以返回特殊位置的ListIterator,通过构造方法传入index,则可以返回一个从该位置开始的迭代器(Iterator)

 /**     * 返回一个特殊位置index的迭代器。     */    public ListIterator<E> listIterator(int index) {        if (index < 0 || index > size)            throw new IndexOutOfBoundsException("Index: "+index);        return new ListItr(index);    }

SubList

在ArrayList中,几乎花了一半的代码来讲SubList,而SubList是什么呢?其实SubList就是ArrayList,读名字读得出来,就是子串,里面的操作基本上是和ArrayList相同,原理也相同,而使用的数组则是直接或间接使用ArrayList里面的elementData 。这里就不多说了。

Java8新增的Spliterator

这里可以看我另一篇博客,里面专门以ArrayListSpliterator分析过:
“集合源码学习(二):Spliterator”

原创粉丝点击