美文网首页程序员
Java集合-ArrayList

Java集合-ArrayList

作者: 懒懒惰惰 | 来源:发表于2017-08-22 10:06 被阅读37次

    分析了一些map的结构,本想继续分析一下迭代器Iterator,但是想想还是用arrayLisy来分析迭代器比较省力,就先分析一下ArrayList

    1. 源码分析

    还是先通过源码来对ArrayList有要一个深入的了解先,首先看他的定义:

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    

    ArrayList继承于AbstractList,实现了List、RandomAccess、Cloneable、Serializable接口,可以看出来,他是一个可以复制、可序列化的数据类型。

    看他的主要成员变量:

    private transient Object[] elementData;
    private int size;
    

    非常简单,定义了一些数组用于存放数据,同时有一个size进行记录数组的指针(也可以理解为数组中存有的数据量大小),众所周知,ArrayList是一个可变的数组,那么初始化情况ArrayList的大小是多少:

    public ArrayList(int initialCapacity) {
        super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        this.elementData = new Object[initialCapacity];
    }
    public ArrayList() { this(10); }
    

    如果未指定数组大小时,默认为10,那么当他容量大于10时,他会怎么扩容呢:

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    

    首先不理会传入的minCapacity,他会将原来的elementData.length直接缩小一半,再加入原来的size大小(即新容量是原容量的1.5倍),同时新容量不能超过系统最大可用的大小(hugeCapacity(minCapacity)).

    那么我们来看一下他的添加数据add():

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    private void ensureCapacityInternal(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    

    add的方法,先检查是否需要扩容和修改计数modCount,这里看到上面的grow()的minCapacity是当前数据量+1,同时在elementData把对象填入指针位置,同时指针位置向后移动一位.

    看一下get方法:

    public E get(int index) {
        rangeCheck(index);
    
        return elementData(index);
    }
    private void rangeCheck(int index) {
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    

    也是很简单易懂的源码,先检查需要取的数据位置是否超过了目前数据指针位置,如果是,就抛出熟悉的越界异常IndexOutOfBoundsException,如果没有,直接返回数据位置的数据对象。

    其他的方法就不一一研究,同时把迭代器Iterator单开一篇进行分析。

    2. 为什么ArrayList线程不安全

    再回过来看一下add方法:

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

    这里的核心是elementData[size++] = e,这里是三个操作:

    get size value;
    elementData[size] = e;
    size++;
    

    那么这就是一个非原子操作,很容易理解,当两个线程同时add数组数据,线程A现将数组中size位置填入A,线程B现将数组中size位置填入B,然后两个线程再分别将size+1,那么可以知道A数据将会丢失。

    相关文章

      网友评论

        本文标题:Java集合-ArrayList

        本文链接:https://www.haomeiwen.com/subject/jlbkdxtx.html