ArrayList源码剖析~

包路径:package java.util;

一、基本属性

1、private  static  final  long  serialVersionUID  =  8683452581122892189L;   

该属性是序列化的时候使用的,serialVersionUID是用来表明类的不同版本之间的兼容性,若一个序列化类没有声明这样一个static final的常量,JVM会根据各种参数为该类计算一个,对于同一个类,不同的JDK版本可能得出不同的serialVersionUID。

 2、private  static  final  int  DEFAULT_CAPACITY  =  10;        数组的默认初始容量大小

 3、private  static  final  Object[ ]   EMPTY_ELEMENTDATA  =  { };       数组的初始化使用

 4、private  transient  Object[ ]  elementData;         ArrayList的底层数据结构是数组

transient关键字:在采用Java默认的序列化机制的时候,被该关键字修饰的属性不会被序列化。

 5、private  int  size;      集合中存储元素的个数

6、protected  transient  int  modCount  =  0;         

在父类AbstractList中定义了该属性:modCount,是用来记录AbstractList结构性变化的次数。在ArrayList所有涉及结构性变化的方法中(增加、删除、修改等)都涉及modCount值++操作。

二、构造函数

1、有参构造如下:传入的参数是数组默认的初始化大小,直接对数组elementData属性进行初始化

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

2、无参构造如下:elementData数组为空

public ArrayList() {
        super();
        this.elementData = EMPTY_ELEMENTDATA;
    }

3、有参构造如下:传入的参数是一个集合类型的对象

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

三、继承关系

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

继承AbstractList类,实现了List<E>等的接口,ArrayList是List接口的一个实现类。

RandomAccess:该接口是一个标志接口,查看该接口的源码会发现,该接口是一个空的,但是只要List集合实现这个接口,支持快速随机访问,我们即可以通过元素的序号快速获取元素对象。总的来说,实现了这个接口的List,使用for循环获取数据优于使用迭代器获取数据,LinkedList没有实现这个接口,那么对于它来说,使用迭代器获取数据优于使用for循环获取数据。

Cloneable:可以进行克隆,

java.io.Serializable:可以进行序列化和反序列化的操作

四、增长方式

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        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);
    }

默认数组的初始化大小是10,按照原数组大小的1.5倍方式进行扩容增长。

两个if语句相当于是给扩容后的大小定了一个上下界。

五、CRUD

1、增加元素的方法(add、addAll)

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

传入的参数是一个:元素

ensureCapacityInternal()方法:首先判断数组是否为空,若为空,数组的容量大小设置为10,确保数组的容量足够存放新加入的元素,若不够,需要进行扩容

将元素插入到elementData数组中,相应的size+1

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

传入的参数是两个:元素和想要添加的位置下标

rangeCheckForAdd():该方法是用来检查传入的位置下标是否合法,不合法抛出下标越界的异常

System.arraycopy():

public static native void arraycopy(Object src,int srcPos,Object dest,int destPos,int length)

将数组src从下标srcPos进行拷贝,一直拷贝length个元素到dest数组中,在dest数组中从destPos下标开始加入先前的srcPos数组。

也就是说此处调用这个方法是要把插入的位置空出来,存储插入的元素

addAll()方法同理上述两个传入参数不同的add()方法,只是传入的参数由数据变成了集合类型的对象。

特点:可以存储重复的数据,也可以存储null

2、删除元素的方法(remove、removeAll、removeRange)

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

按下标删除,首先检查删除下标的合法性,然后找出要删除下标所对应的元素,使用system.arraycopy()方法进行值的挪动拷贝(覆盖掉要删除的下标元素),删除元素意味着size要相应的减少。

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

按元素进行删除,查找元素,找到要删除元素的下标,调用fastRemove()方法,传入删除下标,进行快速删除。

protected void removeRange(int fromIndex, int toIndex) {
        modCount++;
        int numMoved = size - toIndex;
        System.arraycopy(elementData, toIndex, elementData, fromIndex,
                         numMoved);

        // clear to let GC do its work
        int newSize = size - (toIndex-fromIndex);
        for (int i = newSize; i < size; i++) {
            elementData[i] = null;
        }
        size = newSize;
    }

从一个起始位置到结束位置之间的元素全部删除,具体就不再解释,该方法未调用别的陌生的方法,

removeAll():该方法同上述的思想,只是传入的参数是一个集合类型的对象,调用了batchRemove方法进行删除。

特点:数组的起始元素下标是0,找到第一个出现的元素直接删除并返回,时间复杂度:O(n)

3、获取当前元素的方法(get)

public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

通过下标进行获取,首先检查下标的合法性,通过所以位置直接查询数组elementData相对应的元素,时间复杂度:O(1),查询效率很高。

4、遍历ArrayList中的对象

 public Iterator<E> iterator() {
        return new Itr();//返回一个内部类对象
    }

iterator()方法是在AbatractList类当中实现了的,该方法返回AbstractList的一个内部类Itr对象。

六、总结

1、ArrayList基于数组的方式实现,没有容量的限制(扩容)

2、添加元素可能需要扩容(最好首先预判一下)

3、线程不安全

4、内部类:ArrayList有三个内部类

private  class  Itr  implements  Iterator<E>

 private  class  ListItr  extends  Itr  implements  ListIterator<E> 

 private  class  SubList  extends  AbstractList<E>  implements  RandomAccess

Itr实现了Iterator接口,重写了里面的hasNext()、next()、remove()方法。

ListItr继承了Itr而且实现了ListIterator接口,重写了hasPrevious(),nextIndex(),previousIndex(),previous(),set(E e),add(E e)等方法。

SubList继承了AbstractList,实现了RandomAccess接口,类内部实现了对子序列的增删改查等方法,但它同时也充分利用了内部类的优点,就是共享ArrayList的全局变量,例如检查器变量modCount,数组elementData等,所以SubList进行的增删改查操作都是对ArrayList的数组进行的,并没有创建新的数组。

猜你喜欢

转载自blog.csdn.net/qq_40303781/article/details/83749352