ArrayList源码分析

449 阅读7分钟

ArrayList结构和派系

派生关系

Iterable

 集合的最上级接口, 定义了spliterator,iterator和实现了forEach函数

Collection

所有集合的父级接口, 定义了通用函数 

AbstractCollection

实现了Collection中大量函数, 除了特定的几个函数iterator和size之外的函数 

AbstractList

继承了AbstractCollection, 实现了List接口的抽象类, 它实现了List中除size(), get(int location)之外的函数
和AbstractCollection相比, 实现了iterator()接口  

RandomAccess

提供了随机访问(随机查找)功能, 在ArrayList中通过元素的序号快速获取元素对象, 这就是快速随机访问(随机查找)  

List

List有序队列接口, 提供了一些通过下标访问元素的函数.    
List中每一个元素都有一个索引, 第一个元素是0, 往后每次+1

数据结构

ArrayList数据结构是数组
通过一定的算法逻辑动态扩容数组的长度, 可以理解为ArrayList底层是一个动态数组
与java原生的数组相比, 它的容量能动态增长  
内存需要连续的空间保证
除去尾部位置元素的添加, 删除操作, 其余都涉及到位置移动
随机查找效率快(下标搜寻)

初始化

ArrayList源码设计的优雅之道
new ArrayList的时候, 并不会真的创建长度10的对象数组
什么时候往里面添加元素了,什么时候才会完成数组的创建

初始化源码

	private static final long serialVersionUID = 8683452581122892189L;
  
    private static final int DEFAULT_CAPACITY = 10;
 
    private static final Object[] EMPTY_ELEMENTDATA = {};

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    transient Object[] elementData; // non-private to simplify nested class access

    private int size;

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

详细分析

serialVersionUID

用来验证版本一致性的字段, 反序列化时会效验

DEFAULT_CAPACITY

默认容量, 为10 

EMPTY_ELEMENTDATA

空对象数组

DEFAULTCAPACITY_EMPTY_ELEMENTDATA

默认容量空对象数组

elementData

动态数组的空间大小, 包含空元素

size

动态数组的实际大小, 记录数组里有多少元素, 不记录空元素

ArrayList()

无参构造
public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}
把默认容量空对象数组赋值给elementData, 还是空
无参构造创建动态数组时, size为0, elementData为0

ArrayList(int initialCapacity)

带参构造
参数大于0时
	this.elementData = new Object[initialCapacity];
	创建一个大小为initialCapacity的Object数组赋给elementData
参数小与于0时
	this.elementData = EMPTY_ELEMENTDATA;
	开一个长度为10的空间
参数小与于0时
	抛异常

总结

代码测试

    public static void main(String[] args) throws Exception {
        ArrayList  arr = new ArrayList();
        Field field = arr.getClass().getDeclaredField("elementData");
        field.setAccessible(true);
        Object[] o = (Object[]) field.get(arr);
        System.out.println(arr.size());
        System.out.println(o.length);

        arr.add("1");
        field = arr.getClass().getDeclaredField("elementData");
        field.setAccessible(true);
        o= (Object[]) field.get(arr);
        System.out.println(arr.size());
        System.out.println(o.length);

        for (int i = 0; i < 16; i++) {
            arr.add(i);
        }
        field = arr.getClass().getDeclaredField("elementData");
        field.setAccessible(true);
        o= (Object[]) field.get(arr);
        System.out.println(arr.size());
        System.out.println(o.length);
    }

输出

0
0
1
10
17
22

初始化总结 ArrayList arr = new ArrayList(); elementData为0 size为0

ArrayList  arr = new ArrayList(1);
elementData为0
size为1, 走了带参构造, 参数为几, 空间为几, ArrayList(int initialCapacity)   

add("test");//添加元素源码分析会讲为什么空间为10
elementData为10 
size为1
如果第一次添加元素数量为11, 基于动态扩容1.5倍, 它的长度就是15

添加元素

添加元素源码

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

	private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }    

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

详细分析

添加一个元素1-10个元素时

	add(E e)方法 //以无参初始化(ArrayList arr = new ArrayList();), 并调用add(E e)添加一个元素为例
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    
	进ensureCapacityInternal(size + 1);此时传参为1 (0+1=1)
	private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
   
	进calculateCapacity, 判断elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA是否成立
	public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
	条件成立, 因为无参初始化方法已经赋过值
	进if, 判断10和参数哪个大, 10大, 返回10
	private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
  
	此时返回ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); 这里, 参数是10
	10-10不大于0, 没走到动态扩容
	private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
目前是elementData(空间长度为10), size为1(size不记录空元素)

添加元素超出10的情况, 动态扩容

	添加第11个元素时
	public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
	
	此时minCapacity为11
	private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
   
	不走if, 返回11, 则ensureExplicitCapacity(11)
	private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
   
	11-10大于0, 进入动态扩容
	private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
	     // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
	
	进grow(minCapacity); 动态扩容方法
    private void grow(int minCapacity) {
        老空间为10
        int oldCapacity = elementData.length;
        新空间=10+(10>>1), 位运算10的二进制位为1010, 
        右移动1位二进制为101, 10进制为5, 所以新空间为15=10+5
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        如果新空间-11小于0,则新空间为11 
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        如果新空间大于0, 则赋值
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity); //下面讲这个方法
        拷贝一个新的数组和长度的集合,赋值给我们的对象数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    
    
    MAX_ARRAY_SIZE为Integer.MAX_VALUE-8 (int最大值-8)
    如果要开的空间大于MAX_ARRAY_SIZE, 就把int最大值-8赋值给它
    如果不大于MAX_ARRAY_SIZE,则把MAX_ARRAY_SIZE赋值给它
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
    
    
	官方注释对于-8的介绍
		这8bit里存了要分配最大数组的大小(数组下标???)
		虚拟机中在数据中保留的标题字 (既数组形状判断是否为数组,对象是否同步,数组大小, 不知道是否包含了对象类型的类信息指针???)
		尝试分配更大的数组会抛异常, 其实大小还是支持到了Integer.MAX_VALUE
    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

fail-fast失败原因(modCount++)

之前介绍源码的时候把modCount++跳过了, 接下来解释下, 在ArrayList源码里有个监听
源码

    private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
在每次做集合元素操作时,都会改变modCount,在迭代的时候,会把modCount赋值给expectedModCount,  	
如果在迭代过程中删除元素,就会修改modCount,但是迭代器在过程中不会同步expectedModCount,  
 每次迭代会会比较是否相等
 就是说在使用迭代器的时候, 不能改变元素, 但是可以使用迭代器的方法去改变

查找元素

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

        return elementData(index);
    }
    
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    
    E elementData(int index) {
        return (E) elementData[index];
    }
    
判断下标是否越界
复杂度O(1), 直接根据下标位置返回元素

删除元素

删除元素源码

    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;
    }
    
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);
                                        
    src - 源数组。
    srcPos - 源数组中的起始位置。
    dest - 目标数组。
    destPos - 目的地数据中的起始位置。
    length - 要复制的数组元素的数量。

总结

将指定源数组中的数组从指定位置复制到目标数组的指定位置

    比如我们有个数组
    值为 a b c d e f g h i g k
    下标 0 1 2 3 4 5 6 7 8 9 10
    如果删除f
    rangeCheck(index); //效验下标越界, index=5
    int numMoved = size - index - 1; //numMoved= 4
    调用System.arraycopy(elementData, index+1, elementData, index, numMoved);

从原数组下标从6开始copy到新数组从下标从5开始的后面