Java源码篇之容器类——ArrayList

1、前言

对于平常开发的时候遇到的ArrayList,在此做一个简单的源码阅读记录,JDK1.8版本。

2、ArrayList的类关系

在这里插入图片描述

首先看下ArrayList的类关系图,可以看到实现了

  • Serializable接口,支持序列化与反序列化;

  • Cloneable接口,可以被克隆;

  • RandomAccess接口,支持随机访问,另外fori循环会比迭代器循环效率高,代码如下:

    for (int i=0, n=list.size(); i < n; i++)
             list.get(i); 
    

    比这个循环运行得更快:

    for (Iterator i=list.iterator(); i.hasNext(); )
             i.next(); 
    

3、ArrayList的源码

一、类的属性

/**
 * 默认初始容量
 */
private static final int DEFAULT_CAPACITY = 10;

/**
 * (用于空实例的)共享空数组实例
 */
private static final Object[] EMPTY_ELEMENTDATA = {};

/**
 * (用于默认大小的空实例的)共享空数组实例,我们将其与空的元素数据区分开来,
 * 以了解何时添加第一个元素.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 存储ArrayList元素的数组缓冲区。ArrayList的容量是此数组缓冲区的长度。任何
 * elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA elementData 
 * 将在添加第一个元素时扩展到默认容量(10)。
 */
transient Object[] elementData;		// transient关键字标记的成员变量不参与序列化过程

/**
 * ArrayList的大小(包含的元素数)
 */
private int size;

/**
 * 可分配的数组的最大大小,可能会导致OutOfMemory错误:Requested array size exceeds VM limit
 */
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

二、add()方法

// 默认往数组末尾添加元素
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // 大小加1
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {		// minCapacity = size + 1
    // elementData 是成员变量
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

private static int calculateCapacity(Object[] elementData, int minCapacity) {/ 如果
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
		// 数组为空时,返回较大的数
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity; // 数组非空时,返回 minCapacity = size + 1
}

private void ensureExplicitCapacity(int minCapacity) {		// minCapacity = size + 1
    modCount++;		// 计数
    // 数组元素个数加1之后如果大于当前数组长度,则进行扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

private void grow(int minCapacity) {						// minCapacity = size + 1
    int oldCapacity = elementData.length;					// 旧数组的长度
    int newCapacity = oldCapacity + (oldCapacity >> 1);		// 新数组的长度 = 1.5倍旧数组长度
    // 新数组长度小于数组元素个数加1
    if (newCapacity - minCapacity < 0)						
        newCapacity = minCapacity;
    // 新数组长度大于数组最大值
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 创建一个新的数组,并把旧数组元素复制过去,newCapacity为新数组大小
    elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0)		
        // 新数组长度大于数组最大值,并且minCapacity<0才会抛出oom错误
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

// 指定位置添加元素
public void add(int index, E element) {
    // 下标合法性校验
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1); 
    // 生成一个index位置为null的新数组
    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));
}

三、get()方法

public E get(int index) {
    // 检查合法性
    rangeCheck(index);	
    // 返回数组元素
    return elementData(index);
}

private void rangeCheck(int index) {
    // 下标位置大于等于数组长度的时候(数组下标从0开始),抛出下标越界异常
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

E elementData(int index) {
    return (E) elementData[index];
}

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

// 删除指定的元素,第一个出现的
public boolean remove(Object o) {
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {
                fastRemove(index);
                return true;
            }
    }
    return false;
}

private void fastRemove(int index) {
    modCount++;
    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
}

五、set()方法

// 将index位置的元素置换成element
public E set(int index, E element) {
    rangeCheck(index);

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

4、总结

  • 通过阅读源码可以看出,ArrayList的这些方法都没有加锁,所以在多线程环境下是不安全的;
  • add()和remove()方法都需要创建新的数组,是比较消耗性能的;
原创文章 16 获赞 17 访问量 9万+

猜你喜欢

转载自blog.csdn.net/SGdan_qi/article/details/104400975