程序员

Java集合-ArrayList

2017-08-22  本文已影响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数据将会丢失。

上一篇 下一篇

猜你喜欢

热点阅读