ArrayList源码分析

ArrayList

ArrayList基础

  • ArrayList是一个动态数组
  • 线程不安全
  • 实现了RandomAccess接口,可快速访问
  • 实现了Cloneable接口,支持clone操作
  • 实现了Serializable接口,支持序列化

ArrayList初始化

ArrayList的构造方法

//存放初始化的数组
transient Object[] elementData;
//默认的空数组,在传参构造方法中使用
private static final Object[] EMPTY_ELEMENTDATA = {};
//默认的空数组,在无参构造方法中使用
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

/**
 * 无参构造方法,不设置ArrayList初始大小
 */
public ArrayList() {
    //设置当前的数组为空数组,在add的时候才进行初始化
    //使用DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个参数
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
 * 有参构造方法,指定ArrayList初始大小
 * @param: initialCapacity 数组大小
 */
public ArrayList(int initialCapacity) {
    //指定的数组大小大于
    if (initialCapacity > 0) {
        //创建一个大小为initialCapacity的数组
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) { //如果指定的大小等于0
        //设置当前数组为空数组,在add的时候才进行初始化
        //使用EMPTY_ELEMENTDATA这个参数
        this.elementData = EMPTY_ELEMENTDATA;
    } else {//这里为initialCapacity小于0
        //抛出异常,终止初始化
        throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
    }
}

/**
 * 有参构造方法,传入一个集合,将其复制进新的集合中
 * @param: c 准备复制的集合
 */
public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    //如果集合c的长度不为0
    if ((size = elementData.length) != 0) {
        //由于上面elementData设置为了c.toArray,这里防止c.toArray后返回的不是object
        //可以去这个地址查看bug(https://bugs.openjdk.java.net/browse/JDK-6260652)
        if (elementData.getClass() != Object[].class){
            //原数组复制到新数组
            elementData = Arrays.copyOf(elementData, size, Object[].class);  
        }
    } else { //如果为长度为0,进入这里
        //设置为空数组
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

为什么elementData参数要设置为transient

  • 为了让elementData这个参数不会被序列化

为什么要设置两个一样的参数(EMPTY_ELEMENTDATA,DEFAULTCAPACITY_EMPTY_ELEMENTDATA)

  • EMPTY_ELEMENTDATA这个参数在JDK1.7及之前的版本中使用
  • DEFAULTCAPACITY_EMPTY_ELEMENTDATA这个参数在JDK1.8中加入,主要负责性能上的优化,在一定程度上减少了空数组的存在,降低内存消耗

ArrayList序列化

ArrayList实现了Serializable接口,并重写了序列化方法

因为elementData参数设置了transient,避免了序列化,所以ArrayList通过以下两个方法来进行序列化

  • writeObject(java.io.ObjectOutputStream s) -> 序列化,将size和element写入ObjectOutputStream中
//一个全局变量,记录ArrayList的修改次数
protected transient int modCount = 0;

private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException {
    // Write out element count, and any hidden stuff
    // 保存一下当前ArrayList的修改次数
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // 写入当前数组大小
    s.writeInt(size);

    // 写入所有的数组元素
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

    //这里判断,在这个方法运行期间是否有其他线程操作了list
    //如果modCount的值与上面保存的不一样,表示list结构被修改了,抛出异常
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}
  • readObject(java.io.ObjectInputStream s) -> 反序列化,从ObjectInputStream获取size和element,恢复elementData
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    // 读取size
    s.defaultReadObject();

    s.readInt(); 

    //如果数量大于0,才进行读取参数
    if (size > 0) {
        // like clone(), allocate array based upon size not capacity
        SharedSecrets.getJavaObjectInputStreamAccess().checkArray(s, Object[].class, size);
        Object[] elements = new Object[size];

        // 读取elements
        for (int i = 0; i < size; i++) {
            elements[i] = s.readObject();
        }
        elementData = elements;
    } else if (size == 0) { //如果大小等于0,那么直接赋值为空数组
        elementData = EMPTY_ELEMENTDATA;
    } else { //如果小于0,则抛出异常,反序列化失败
        throw new java.io.InvalidObjectException("Invalid size: " + size);
    }
}

ArrayList取值

  • ArrayList通过数组的索引取值,传入index值,返回当前位置的元素
/**
 * 对外开放的get方法
 * @param: index 数组索引
 */
public E get(int index) {
    //判断index的值是否超出了size值
    //如果索引大于了数组长度,抛出异常
    Objects.checkIndex(index, size);
    return elementData(index);
}

/**
 * 对内真正的get方法
 */
E elementData(int index) {
    //根据index直接在数组查找,并根据传入的类型E进行返回
    return (E) elementData[index];
}
  • Objects.checkIndex()
//Objects类
@ForceInline
public static int checkIndex(int index, int length) {
    return Preconditions.checkIndex(index, length, null);
}
//Preconditions类
@HotSpotIntrinsicCandidate
public static <X extends RuntimeException>
    int checkIndex(int index, int length,
                   BiFunction<String, List<Integer>, X> oobef) {
    //如果index超出了长度,则抛出异常
    if (index < 0 || index >= length)
        throw outOfBoundsCheckIndex(oobef, index, length);
    return index;
}

ArrayList新增

ArrayList新增方法

/**
 * 对外开放的新增方法
 * @param: E e 新增的元素
 */
public boolean add(E e) {
    //新增一次ArrayList操作次数
    modCount++;
    //调用真正的新增方法
    add(e, elementData, size);
    return true;
}

/**
 * 真正的新增方法
 * @param: E e 新增的元素
 * @param: elementData ArrayList数组
 * @param: s 当前数组的元素个数
 */
private void add(E e, Object[] elementData, int s) {
    //如果size等于数组的长度了,进行扩容
    //这里还有一个作用,当数组长度为0的时候,进行数组初始化
    //默认创建空数组,优化了空间利用率
    if (s == elementData.length)
        //调用扩容方法
        elementData = grow();
    //设置size位置的值为新增元素
    elementData[s] = e;
    //size++
    size = s + 1;
}

/**
 * 在指定位置新增
 * @param: index 数组索引
 * @param: element 新增元素
 */
public void add(int index, E element) {
    //index值进行校验,判断是否符合规定
    rangeCheckForAdd(index);
    //ArrayList操作次数+1
    modCount++;
    final int s;
    Object[] elementData;
    //数组扩容
    if ((s = size) == (elementData = this.elementData).length){
        elementData = grow();
	}
    //数组复制,将要插入的元素的位置空出来
    //将index位置后的元素都向后移动1位
    //第一行为原数组,原数组起始位置
    System.arraycopy(elementData, index, 
                     //新数组,新数组起始位置
                     elementData, index + 1, 
                     //复制的长度
                     s - index);
    //数组元素替换
    elementData[index] = element;
    //数组长度+1
    size = s + 1;
}

/**
 * 对新增的参数索引进行校验
 * @param: index 数组索引
 */
private void rangeCheckForAdd(int index) {
    //如果index大于当前数组容量,或index小于0
    if (index > size || index < 0)
        //抛出数组越界异常
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

ArrayList扩容

/**
 * 无参扩容方法,在内部调用有参构造方法
 */
private Object[] grow() {
    return grow(size + 1);
}

/**
 * 有参扩容方法,内部调用真正的扩容方法
 * @param: minCapacity 数组需要的最小容量
 */
private Object[] grow(int minCapacity) {
    //调用数组复制方法,newCapacity(minCapacity)返回新数组的长度
    return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity));
}

/**
 * 真正的扩容方法
 * @param: minCapacity 数组需要的最小容量
 */
private int newCapacity(int minCapacity) {
    //保存原始的数组长度
    int oldCapacity = elementData.length;
    //新数组长度等于旧的长度 + (旧长度/2)
    //如果oldCapacity为奇数,则(oldCapacity >> 1)为oldCapacity/2-1
    //如果oldCapacity为偶数,则(oldCapacity >> 1)为oldCapacity/2
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //如果新长度小于了目前需要的最小长度
    if (newCapacity - minCapacity <= 0) {
        //如果当前为空数组
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            //DEFAULT_CAPACITY = 10
            //返回DEFAULT_CAPACITY或minCapacity中最大的那个
            //数组初始化也走这里
            return Math.max(DEFAULT_CAPACITY, minCapacity);
		//如果minCapacity小于0,则表示数据不符合规则
        if (minCapacity < 0) 
            //数组为负数,数组越界,抛出异常
            throw new OutOfMemoryError();
        return minCapacity;
    }
    //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
    //如果新长度小于数组最大阈值,则直接返回
    return (newCapacity - MAX_ARRAY_SIZE <= 0)
        ? newCapacity
        //如果大于数组阈值,计算返回结果
        : hugeCapacity(minCapacity);
}

/**
 * 如果数组的新长度大于数组阈值,执行这个方法
 * @param: minCapacity 数组需要的最小容量
 */
private static int hugeCapacity(int minCapacity) {
	//小于0,数组越界
    if (minCapacity < 0)
        throw new OutOfMemoryError();
    //如果数组需要的值大于(MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8)
    return (minCapacity > MAX_ARRAY_SIZE)
        //返回Integer的最大值
        ? Integer.MAX_VALUE
        //返回MAX_ARRAY_SIZE
        : MAX_ARRAY_SIZE;
}

ArrayList更新

/**
 * ArrayList更新
 * @param: index 数组索引
 * @param: element 更新的元素
 */
public E set(int index, E element) {
    //验证index是否符合规定
    Objects.checkIndex(index, size);
    //将原始值保存
    E oldValue = elementData(index);
    //新值替换旧值
    elementData[index] = element;
    return oldValue;
}

ArrayList删除

/**
 * ArrayList删除 - 根据索引删除
 * @param: index 数组索引
 */
public E remove(int index) {
	//判断索引是否符合规则
    Objects.checkIndex(index, size);
    //保存一个删除前的数组
    final Object[] es = elementData;
    //根据index获取当前位置的元素
    E oldValue = (E) es[index];
    //调用真正的删除方法
    fastRemove(es, index);
    return oldValue;
}

/**
 * ArrayList删除 - 根据元素删除
 * @param: o 数组元素
 */
public boolean remove(Object o) {
    //获取当前list数组
    final Object[] es = elementData;
	//获取数组元素个数
    final int size = this.size;
    int i = 0;
    //先查找要删除的元素是否存在
    //将下面括号里的代码声明给found
    found: {
        //如果传入的元素为空
        if (o == null) {
            //循环寻找
            for (; i < size; i++)
                //如果找到了,退出循环
                if (es[i] == null)
                    break found;
        } else { //如果传入元素不为空
            //循环查找
            for (; i < size; i++)
                //如果找到,退出循环
                if (o.equals(es[i]))
                    break found;
        }
        //如果没找到,直接结束删除方法,返回false
        return false;
    }
    //调用真正的删除方法
    fastRemove(es, i);
    return true;
}

/**
 * ArrayList的真正删除方法
 * @param: es 要删除的数组
 * @param: i 数组索引
 */
private void fastRemove(Object[] es, int i) {
    //ArrayList操作次数+1
    modCount++;
    final int newSize;
    //数组长度大于索引
    if ((newSize = size - 1) > i)
        //数组index后面的元素往前移动一位
        System.arraycopy(es, i+1, es, i, newSize - i);
    //数组的最后一个位置置空
    es[size = newSize] = null;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值