ArrayList理解

1. ArrayList底层实现

	底层是通过数组实现的,所以查找元素快,但增删元素比较慢。ArrayList的容
量是动态的,在每次添加元素的时候都会校验索引是否超出容器的长度,如果超出,
就把当前数组的元素复制一份到新数组中,并且新数组的长度是原数组的1.5倍。

2. 核心属性

 	/**
     *  默认数组初始长度
     */
    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;

3. 构造方法

	/**
	*无参构造器,elementData指向空数组(DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
	*/
	public ArrayList() {
		this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
	}

    /**
     * 有参构造器,当传入初始长度时,会创建一个该长度的Object数组给 elementData 
     * 如果长度为0,则elementData 指向空数组(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);
        }
    }

	/**
     * 
     * 传入一个集合元素,并将该集合元素转为数组,给elementData
     */
	public ArrayList(Collection<? extends E> c) {
	        elementData = c.toArray();
	        if ((size = elementData.length) != 0) {		//如果数组长度不为0
	            if (elementData.getClass() != Object[].class)	//如果不是Object[],就将原数组的元素复制一份到新的Object[]
	                elementData = Arrays.copyOf(elementData, size, Object[].class);
	        } else {
	            this.elementData = EMPTY_ELEMENTDATA;	//数组长度为0时
	       }
	}

4. 常用方法

4.1 add(E)

	public boolean add(E e) {
		//第1次添加数据:ensureCapacityInternal(0 + 1);
		//第2次添加数据:ensureCapacityInternal(1 + 1);
		//第11次添加数据:ensureCapacityInternal(10 + 1);
        ensureCapacityInternal(size + 1);  
        elementData[size++] = e;
        //第1次添加完成后 elementData={1, , , , , , , , , }
        //第2次添加完成后 elementData={1,2, , , , , , , , }
        //第11次添加完成后 elementData={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, , , , }
        return true;
    }

	private void ensureCapacityInternal(int minCapacity) {
		//第1次添加数据:ensureExplicitCapatity(calculateCapacity({},1)); => ensureExplicitCapatity(10)
		//第2次添加数据:ensureExplicitCapatity(calculateCapacity({1, , , , , , , , , },2)); => ensureExplicitCapatity(2)
		//第11次添加数据:ensureExplicitCapatity(calculateCapacity({1,2,3,4,5,6,7,8,9,10 },11)); => ensureExplicitCapatity(11)
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

	private static int calculateCapacity(Object[] elementData, int minCapacity) {
		//第1次添加数据:{} == {} 成立,return Math.max(10, 1);  返回10
		//第2次添加数据:{1, , , , , , , , , } == {} 不成立,return 2;
		//第11次添加数据:return 11;
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        //第1次添加数据:10 - 0 > 0	触发grow(10)
        //第2次添加数据:2 - 10 < 0,不触grow
        //第11次添加数据:11 - 10 > 0 触发grow(11)
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

	 private void grow(int minCapacity) {
        int oldCapacity = elementData.length;	//记录当前数组长度
        int newCapacity = oldCapacity + (oldCapacity >> 1);	//新数组长度 = 当前数组长度+当前数组长度右移1位,结果为原长度的1.5倍
        
        if (newCapacity - minCapacity < 0)		//如果新数组长度小于入参
            newCapacity = minCapacity;		
        if (newCapacity - MAX_ARRAY_SIZE > 0)	//新数组长度大于数组最大长度
            newCapacity = hugeCapacity(minCapacity);
        // 将原数组复制一份,长度为newCapacity,并返回新数组
        //第1次添加数据:Arrays.copyOf({}, 10) => new Object[10];
        //第11次添加数据:Arrays.copyOf(elementData, 15) => {1,2,3,4,5,6,7,8,9,10, , , , , }
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

4.2 add(index, E)

	public void add(int index, E element) {
        rangeCheckForAdd(index);	//判断索引是否超出已有元素的长度

        ensureCapacityInternal(size + 1);  // 判断是否需要扩容
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }

	private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
假设elementData = {1,2,3,4,5,6, , , , }
实际元素个数:size = 6;
需要插入的位置:index = 2,即数组元素3所在的位置
需要移动的元素数量:numMoved = size - index = 4,即3,4,5,6
执行 System.arraycopy(elementData, index, elementData, index + 1, size - index);
结果:
elementData = {1,2, ,3,4,5,6, , , }

4.3 remove(index)

 	public E remove(int index) {
        rangeCheck(index);		//判断索引是否超出元素个数

        modCount++;
        E oldValue = elementData(index);	//获取要删除的元素

        int numMoved = size - index - 1;	//计算从被删除的元素后面还有几个元素
        if (numMoved > 0)
        //将源数组(第一个参数)的第 index + 1 位置的 numMoved个元素,移动至目标数组(第三个参数)的 第index 位置
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);	
        elementData[--size] = null; 
        return oldValue;
    }

	private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
	}
假设elementData = {1,2,3,4,5,6, , , , } 
实际元素个数:size=6
需要移除元素的索引:index = 2
需要移动的元素个数:numMoved = 6 - 2 - 1 = 3
执行System.arraycopy(elementData, index+1, elementData, index, numMoved);
结果:
执行前的elementData = {1,2,3,4,5,6, , , , };
执行后的elementData = {1,2,4,5,6,6, , , , };
elementData[--size] = null;
最终elementData = {1,2,4,5,6, , , , , };

4.4 get(index)

	public E get(int index) {
        rangeCheck(index);		//检查index是否超出实际元素的长度

        return elementData(index);
    }
    
    E elementData(int index) {
        return (E) elementData[index];	//从数组的对应位置返回元素
    }

5. FailFast机制

相关属性

protected transient int modCount = 0;
	modCount是ArrayList 的父类AbstractList<E> 中的属性,当执行添加、
删除操作时,该属性会自增(set(index, E)方法并不会导致自增)。当使用Iter
ator 迭代时,会把modCount赋值给迭代器中的expectedModCount属性。
	当多个线程共享一个list 时,每次调用next() 都会检查modCount和
expectedModCount的值是否相等,如果不相等,则表明有线程对list执行了添加、
删除操作。
	快速失败机制,java集合类为了应对并发访问,在集合迭代过程中,内部结构
发生变化的一种防护措施。该错误检查机制为这种可能发生的错误抛出
java.util.ConcurrentModificationException

也就是说在使用迭代器进行迭代时,是不允许对集合进行修改操作的。

总结

	ArrayList通过Object[] 存放数据,size是数组中实际元素的个数。所有的
操作都是围绕着数组的扩容、赋值来进行的。

	添加元素时,会对当前数组的长度进行判断,当满足扩容条件时,就会把当前
数组的元素复制一份到新的数组中,并且长度是原来的1.5倍。

	在指定index添加元素,其实就是先把该位置及后面的元素都往后挪一位,然
后再给这个index赋值。

	删除指定index的元素,其实就是把该index后面的元素都往前挪一位,然后把
最后一位设为null。

	快速失败机制在并发访问共享的集合时提供了检查机制,但如果不使用迭代器
该机制也无法正常生效(使用foreach 可以生效,因为foreach本质是对迭代器的
调用,下面给出使用foreach的反编译代码)。
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space 
// Source File Name:   ThreadRead.java

package org;

import java.util.Iterator;
import java.util.List;

public class ThreadRead extends Thread
{

	private List list;

	public ThreadRead(List list)
	{
		this.list = list;
	}

	public void run()
	{
	//通过反编译工具可以看到,实际上foreach调用的是Iterator 
		for (Iterator iterator = list.iterator(); iterator.hasNext();)
		{
			Object obj = iterator.next();
			try
			{
				Thread.sleep(5L);
			}
			catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		}

	}
}

目前整理的就这么多,后期有新的理解会回来补充。
水平有限,欢迎各位吐槽评论,谢谢。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值