ArrayList 源码分析

本质:就是动态数组,动态扩容
1.内存地址连续,使用之前必须要指定数组长度
2.可以通过下标访问的方式访问成员,查询效率高
3.增删操作会给系统带来性能消耗[保证数据下标越界的问题,需要动态扩容]
增删:总是要copy,移动元素,效率低
查询:通过下标访问,效率很高

一、构造器

// 了解重要的基本属性 -- 方便理解源码

// 默认的数组的长度-容量
private static final int DEFAULT_CAPACITY = 10;
// 空数组 - 
private static final Object[] EMPTY_ELEMENTDATA = {};
// 空对象 first element is added.
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 集合中实际存储数据的数组对象
transient Object[] elementData;
// 集合中元素的个数 
private int size;

// 无参构造器
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
        // this.elementData = {}
}

// 有参构造器
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        // 初始长度大于0 就创建一个指定大小的数组
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        // {}数组赋值给 this.elementData
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

二、重要方法

先分析add方法之后,在看看其他方法,基本就差不多啦,重点关注ensureCapacityInternal计算容量

  1. add+ensureCapacityInternal
// add(E e)
public boolean add(E e) {
    // 确定容量 动态扩容 size 初始 0
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // 将要添加的元素 添加到数组中 elementData[0] = 1 --> size = 1
    elementData[size++] = e;
    return true;
} 
// 核心 -- 动态扩容
// ensureCapacityInternal
    private void ensureCapacityInternal(int minCapacity) {
            ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    // calculateCapacity
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            // 10  1 return 10
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        // 5
        return minCapacity;
    }
    //ensureExplicitCapacity
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++; // 增长 操作次数

        // minCapacity 10
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    // grow
    private void grow(int minCapacity) { // 10
        // overflow-conscious code
        int oldCapacity = elementData.length; // 0
        // newCapacity = 0
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            // newCapacity = 10 
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // {}   {,,,,,,,,,} 返回一个新的数组 长度为10
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
  1. 其他重要方法

    // add(int index, E element)
    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++;
    }
    // remove
    public E remove(int index) {
        rangeCheck(index);
    
        modCount++;
        E oldValue = elementData(index);
    	// 获取要移动的元素的个数 {1,2,3,4,5,6,7,8,9} // 3  size=9  index=3
        //  {1,2,3,5,6,7,8,9,null}
        int numMoved = size - index - 1; // 5
        if (numMoved > 0)
            // 源数组 开始下标 目标数组 开始下标 长度
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
    	// 删除的节点对应的信息
        return oldValue;
    }
    
    public E get(int index) {
        // 检查下标是否合法
        rangeCheck(index);
    	// 通过下标获取数组对应的元素
        return elementData(index);
    }
    
    public E set(int index, E element) {
        rangeCheck(index); // 检查下标
    	// 获取下标原来的值
        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
    

三、自己的理解

自己理解性的文字,以及需要掌握的重要代码

初始化:
    new ArrayList( initialCapacity )
    
//存储数据的对象 this.elementData
// 无参构造区
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

// 参数 0
this.elementData = EMPTY_ELEMENTDATA;
// 参数 大于 0
this.elementData = new Object[initialCapacity];

add(E e){
    /** 
    	容量计算
    	ensureCapacityInternal
    	计算容量 calculateCapacity(size+1) //size 开始为 0 ,每次都会变化,等同于数组实际数据大小
    			- 无参  10以内  return 10 //默认给一个容量
    			       大于10  return size+1 
    			- 有参  return size+1 
   	    确保容量 ensureExplicitCapacity(size + 1)
    		     大于数组容量:扩容--每次扩容到原来的1.5倍 -- 通过位运算 -- 第一次传多少是多少,第二次							  才有可能出现扩容
    		     最大容量不超过 Integer.MAX_VALUE 
    		     	-- hugeCapacity 
    		     	 (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;                  新容量大小:newCapacity
      		     对象变化:this.elementData = Arrays.copyOf(elementData, newCapacity);
	**/
	elementData[size++] = e;
}

add(index,e){
      // add 只能是替换某个位置的元素,因为必须是 index < size
      // rangeCheckForAdd
      if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
	  // arraycopy
       System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
		// 赋值
        elementData[index] = element;
}

remove(index){
         // arraycopy
         System.arraycopy(elementData, index+1, elementData, index, numMoved);
         //  index 位置 给 null  -- GC          
         elementData[--size] = null; // clear to let GC do its work
}

// get 就是数组通过index操作
get(index){
    return  elementData[index];
}
// set 就是数组通过index操作
set(index){
    elementData[index] = element;
}
    

四、用例

// 会抛出异常
List arr = new ArrayList<>(5);
arr.add(3,1);

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 3, Size: 0

五、补充:FailFast机制

​ 快速失败的机制,Java集合类为了应对并发访问在集合迭代过程中,内部结构发生变化的一种防护措施,这种错误检查的机制为这种可能发生错误通过抛出 java.util.ConcurrentModificationException

说白了就是list不能一边遍历一边修改

// ArrayList有个内部类Itr实现了Iterator接口
// ArrayList的每次操作modCount都会增加,如果另一个线程也做了修改,
// 可能会导致modCount不等于expectedModCoun 
//next()
final void checkForComodification() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
}
    /**
     * 解决方式:
     * 使用Iterator的remove()方法
     * 使用for循环正序遍历 - 需要修正index ,不然会漏掉个别元素
     * 使用for循环倒序遍历
     */
// 不能使用增强for循环-因为使用的就是内部迭代器iteratorhasNext和next()方法来判断和取下一个元素。
posted @ 2020-11-13 23:18  2步  阅读(99)  评论(0编辑  收藏  举报