【ArrayList】源码笔记

ArrayList是可变长的数组,在实际开发中十分常用,通过分析源码和解析之后记录一下自己的理解。


ArrayList允许存入null;

ArrayList线程不同步;


继承(实现)关系

extends

|---AbstractList<E>

implements

|---List<E>
|---RandomAccess
|---Cloneable
|---java.io.Serializable


字段

   
    //序列化的版本号
    private static final long serialVersionUID = 8683452581122892189L;

    //默认的初始容量
    private static final int DEFAULT_CAPACITY = 10;

    //以含参构造函数创建且指定长度为0时使用的数组实例
    private static final Object[] EMPTY_ELEMENTDATA = {};

    //以空参构造函数创建时使用的数组实例
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    //存放元素的数组的引用
    transient Object[] elementData;

    //实际存放元素的个数
    private int size;

构造方法

1.无参构造

    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

解析:在使用无参构造创建ArrayList实例时,底层数组引用elementData会指向长度为0的数组实例DEFAULTCAPACITY_EMPTY_ELEMENTDATA,此时的容量为0。

2.含参构造①

    public ArrayList(int initialCapacity) {
        
        //如果指定的初始容量大于0,实例化一个长度为initialCapacity的数组,并赋值给elementData
        if (initialCapacity > 0) {

            this.elementData = new Object[initialCapacity];

        } else if (initialCapacity == 0) {

            /*  如果指定的初始化容量等于0,底层数组引用elementData会指向长度为0的数组实例
             *  EMPTY_ELEMENTDATA    
             */                 
            this.elementData = EMPTY_ELEMENTDATA;
        } else {

            //如果指定的初始化容量小于0,抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

解析:该构造方法可以指定初始容量,传入的参数initialCapacity即为初始容量。

3.含参构造②

    public ArrayList(Collection<? extends E> c) {
        
        //将集合c转化为数组,赋值给elementData
        elementData = c.toArray();

        //如果数组长度不等于0
        if ((size = elementData.length) != 0) {
            
            //如果得到的数组不是一个Object[]类型的数组,将其转化为Object[]
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        
        } 
        else {
            //如果数组长度为0,element指向EMPTY_ELEMENTDATA
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

解析:该构造方法可以构造一个包含指定集合元素的集合。



动态增长机制:(扩容过程)

以添加一个元素的方法add(E e)为例分析ArrayList的动态增长机制

1.调用add(E e)方法增加1一个元素。

2.在add(E e)方法内部会调用ensureCapacityInternal方法。判断集合是否是以空参构造实例化的,如果是,将默认的初始容量(DEFAULT_CAPACITY=10)作为需要的最小长度;如果不是,则以size+1作为需要的最小容量。

private void ensureCapacityInternal(int minCapacity) {

        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

3.在ensureCapacityInternal方法内部调用ensureExplicitCapacity方法。判断最少需要的容量是否超过了当前数组的长度。

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

4.如果最少需要的容量是否超过了当前数组的长度,调用grow方法进行扩容。尝试将原容量的1.5倍作为新的容量newCapacity ,如果新的容量newCapacity还是小于需要的最小容量minCapacity,那就将需要的最小长度minCapacity作为新的容量。接着判断新的容量是否大于MAX_ARRAY_SIZE(Integer.MAX_VALUE-8),如果超过了,就将Integer.MAX_VALUE作为新容量,否则将MAX_ARRAY_SIZE(Integer.MAX_VALUE-8)作为新容量,将原数组赋值到长度为newCapacity的新数组中,此时数组长度才真正发生了改变,扩容完成。

private void grow(int minCapacity) {
        
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) 
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }


主要方法

1.add(E e):添加一个元素到末尾  O(1)

    public boolean add(E e) {

        //判断是否扩容
        ensureCapacityInternal(size + 1);
        //将元素存放到末尾    
        elementData[size++] = e;
        return true;
    }

2.add(int  index,E e):将元素添加到指定的位置 O(n)

    public void add(int index, E element) {

        //判断是否越界
        rangeCheckForAdd(index);

        //判断是否需要扩容
        ensureCapacityInternal(size + 1);  

        //将将index位置之后的元素后移
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);

        //将元素存放到index位置上
        elementData[index] = element;

        size++;
    }

3.addAll(Collection<? Extends E> c):将一个集合中的元素按迭代顺序添加到末尾

    public boolean addAll(Collection<? extends E> c) {
        
        //将集合c转为数组a
        Object[] a = c.toArray();

        //记录数组长度numNew
        int numNew = a.length;

        //判断是否需要扩容
        ensureCapacityInternal(size + numNew); 
        
        //将数组a的复制到原数组或扩容后的数组中
        System.arraycopy(a, 0, elementData, size, numNew);

        //更新元素个数size
        size += numNew;
        return numNew != 0;
    }

4.addAll(int index, Collection<? Extends E> c):将一个集合中的元素按迭代顺序添加到指定位置

    public boolean addAll(int index, Collection<? extends E> c) {
        //判断index是否越界
        rangeCheckForAdd(index);

        //将集合c转为数组a
        Object[] a = c.toArray();

        //记录数组长度numNew
        int numNew = a.length;

        //判断是否需要扩容
        ensureCapacityInternal(size + numNew); 

        //计算需要移动的元素数量numMoved
        int numMoved = size - index;
        
        //如果numMoved>0,将index位置及其之后的元素后移numMoved个位置
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);
    
        //将a数组中的元素赋值到从index位置开始的numMoved个位置上
        System.arraycopy(a, 0, elementData, index, numNew);

        //更新元素个数size为size+numNew
        size += numNew;
        return numNew != 0;
    }

5.remove(int index):删除指定位置上的元素 O(n)

    public E remove(int index) {

        //判断index是否越界
        rangeCheck(index);

        modCount++;

        //将index位置上的元素记录下来
        E oldValue = elementData(index);

        //将index位置之后的元素前移(覆盖index位置上元素)
        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);

        //将末尾元素置空(=null)
        elementData[--size] = null;

        //返回记录的index位置上的元素(oldValue)
        return oldValue;
    }

6.remove(Object o):删除第一个出现的指定的元素

    public boolean remove(Object o) {

        //o如果为null,遍历数组并删除第一个为空的元素
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
        //o如果不为null,遍历数组并删除第一个与o相同的元素
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }

        //如果没有找到该元素,返回false
        return false;
    }

7.removeAll(Collection<?> c):删除指定集合中包含的所有元素

    public boolean removeAll(Collection<?> c) {

        //判断集合c是否为null,如果为null,抛出NullPointerException
        Objects.requireNonNull(c);

        return batchRemove(c, false);
    }

    private boolean batchRemove(Collection<?> c, boolean complement) {

        
        final Object[] elementData = this.elementData;

        //维护两个指针r,w,r用来遍历数组elementData,w用来记录不删除的元素存放位置
        int r = 0, w = 0;

        boolean modified = false;

        try {
            //遍历数组elementData中所有元素,依次判断是否存在于集合c中,如果没有,将其复制到w指针位置,w指针后移。
            for (; r < size; r++)
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            
            //判断r和w的位置,如果r没有移动到末尾(产生了异常中断了程序),将r指针及其之后的位置上的n元素向前移动到w指针位置,w指针后移n个位置。
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
            //将w指针及其之后的元素置为null
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }

8.set(int index,E e):将指定位置上的元素替换为指定元素  O(1)

    public E set(int index, E element) {

        //判断index是否越界
        rangeCheck(index);

        //将index位置上的元素记录下来
        E oldValue = elementData(index);

        //将e覆盖到index位置
        elementData[index] = element;

        //返回记录的index位置上的元素
        return oldValue;
    }

9.get(int index):获取指定位置上的元素

    public E get(int index) {

        //判断index是否越界
        rangeCheck(index);
        
        return elementData(index);
    }

    @SuppressWarnings("unchecked")
    E elementData(int index) {
        //返回指定位置上的元素
        return (E) elementData[index];
    }

10.clear():删除所有元素

    public void clear() {
        //修改计数+1
        modCount++;

        // 将所有元素置空
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

11.trimToSize():将集合容量修改为当前元素个数

    public void trimToSize() {
        //修改计数+1
        modCount++;

        //如果元素个数小于容量
        if (size < elementData.length) {

            //如果元素个数为0,将数组置为EMPTY_ELEMENTDATA,如果元素个数不为0,将数组长度调整为与元素个数相同
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

12.indexOf(Object o):返回此集合中指定元素的第一次出现的索引,如果此列表不包含该元素,返回-1。

    public int indexOf(Object o) {

        //如果o为null,遍历数组并返回第一个为null的元素的位置
        if (o == null) {
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {

         //如果o不为null,遍历数组并返回第一个与o相同的元素的下标
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }

        //如果没有找到,返回-1
        return -1;
    }

13.lastIndexOf(Object o):返回此集合中指定元素的最后一次出现的索引,如果此列表不包含该元素,返回-1。

    public int lastIndexOf(Object o) {

        //如果o为null,反序遍历数组并返回第一个为null的元素的位置
        if (o == null) {
            for (int i = size-1; i >= 0; i--)
                if (elementData[i]==null)
                    return i;
        } else {

        //如果o不为null,反序遍历数组并返回第一个与o相同的元素的下标
            for (int i = size-1; i >= 0; i--)
                if (o.equals(elementData[i]))
                    return i;
        }
        //如果没有找到,返回-1
        return -1;
    }

14.contains(Object o):如果此列表包含指定的元素,则返回 true。

    public boolean contains(Object o) {

        调用indexof(Object o),返回结果与0比较,返回比较结果。
        return indexOf(o) >= 0;
    }

猜你喜欢

转载自blog.csdn.net/QiuBika_061/article/details/90260994