ArrayList源码分析

ArrayList是日常开发中使用非常频繁的容器,今天看一下它的源码。ArrayList的源码其实很简单,它就是一个动态数组,我们先从new开始看起:

   /**
     * Constructs an empty list with an initial capacity of ten.
     */    
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

上述是ArrayList的一个无参构造器,我们来分别看一下elementData和DEFAULTCAPACITY_EMPTY_ELEMENTDATA分别是什么意思:

 /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access
/**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

根据以上的代码可以看出,DEFAULTCAPACITY_EMPTY_ELEMENTDATA是静态的、final的一个空数组变量。其实new ArrayList();就是创建了一个空的数组。DEFAULTCAPACITY_EMPTY_ELEMENTDATA是静态的、final的一个空数组变量。elementData就是我们实际要操作的那个数组对象。无参构造非常的简单,然后它也有一个有参构造方法:
 

   /**
     * Constructs an empty list with the specified initial capacity.
     *
     * @param  initialCapacity  the initial capacity of the list
     * @throws IllegalArgumentException if the specified initial capacity
     *         is negative
     */
    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);
        }
    }

上述的代码,很容易可以看出是指定数组长度的一个有参构造方法,如果指定的大小大于0就创建一个指定长度的对象数组,如果等于零就创建一个空数组,EMPTY_ELEMENTDATA定义如下:

    /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

否则就抛出非法容量的异常。

还有一个构造方法:

 /**
     * Constructs a list containing the elements of the specified
     * collection, in the order they are returned by the collection's
     * iterator.
     *
     * @param c the collection whose elements are to be placed into this list
     * @throws NullPointerException if the specified collection is null
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

上述构造方法就是通过一个集合来创建一个新的数组,很简单,不再过多介绍。然后我们看下ArrayList常用的几个方法的源码。

首先add()方法是最常用的方法之一,源码如下:

    /**
     * Appends the specified element to the end of this list.
     *
     * @param e element to be appended to this list
     * @return <tt>true</tt> (as specified by {@link Collection#add})
     */
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 检查容量是否合法, Increments modCount!!
        // 数组扩容后将数据添加到合适的位置  并size+1
        elementData[size++] = e;
        return true;
    }
     private void ensureCapacityInternal(int minCapacity) {
        // 如果是第一次 true
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {                
            //如果是空数组的话我们就比较输入长度和默认的长度的大小
            //private static final int DEFAULT_CAPACITY = 10; 默认长度为10
            //使用Math的函数输出大的那个,做为指定的容量
            // 第一次为10
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        // 确保明确的容量,扩容
        ensureExplicitCapacity(minCapacity);
    }

    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;    //记录修改的次数  protected transient int modCount = 0;
        //modCount是一个计数器,在多线程操作的情况下来判断这个list是否被别的线程操作过,如果操                                
        //作过会抛出ConcurrentModificationException
            
        // 第一次 10 - 0 > 0
        if (minCapacity - elementData.length > 0)//这里判断当前的容量减去原数组的长度如果大于0说明需要扩容了,于是调用 grow(minCapacity)进行扩容操作
            // 扩容
            grow(minCapacity);
    }

    /**
     * Increases the capacity to ensure that it can hold at least the
     * number of elements specified by the minimum capacity argument.
     *
     * @param minCapacity the desired minimum capacity
     * 其实就是再创建一个更大的数组,并将原来的元素复制过去,扩容大小为原来的大小加上原来大小的一半 int newCapacity = oldCapacity + (oldCapacity >> 1);
     */
    private void grow(int minCapacity) {
        // 记录原来的容量
        int oldCapacity = elementData.length;
        // 计算新的容量 新容量为 老容量的1.5倍 第一次为0
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 把原来数组中的内容拷贝到一个新建的指定容量为newCapacity的数组中,扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

get方法:获取集合中的元素

     public E get(int index) {
        // 检查下标是否合法
        rangeCheck(index);
        // 通过下标从数组中返回对应的元素
        return elementData(index);
    }

    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

set方法:修改集合中的元素

public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

remove方法:移除指定的元素

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

add(index,ele)方法:

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

FailFast机制

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

就是检查前面提到的modCount变量

效果:

public class ThreadIterate extends Thread {

    private List list;

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

    @Override
    public void run() {
        while(true){
            for (Iterator iteratorTmp = list.iterator();iteratorTmp.hasNext();) {
                iteratorTmp.next();
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
public class ThreadMain {

    private static List list = new ArrayList();

    public static void main(String[] args) {
        new ThreadAdd(list).start();
        new ThreadIterate(list).start();
    }
}
public class ThreadAdd extends Thread{

    private List list;

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

    public void run(){
        for (int i = 0; i < 100; i++) {
            System.out.println("loop execute : " + i);
            try {
                Thread.sleep(5);
                list.add(i);
            }catch (Exception e){

            }
        }
    }
}

这是就ArrayList的failFast机制,也就是在多线程操作一个List数据的时候如果出现数据安全问题会直接抛异常。

注:FailFast机制图片摘自咕泡P5课程笔记

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值