源码分析笔记ArrayList(JDK1.8)

终于开始写博客了,前面想了很多次写博客,但是都没有动笔,苦于不知如何表述,最近开始复习集合,想通过写博客加深自己的印象,记录自己的理解,该篇文章从构造方法入手,然后到常用的一些方法,摘取JDK中源码,在上面加上自己的理解,有不对的不严谨的地方还望大佬们多多指正

概述
  • 继承AbstractList抽象类,实现List接口
  • 底层用数组存放数据,默认容量大小为10
  • 扩容倍数为1.5倍
  • 线程不安全的
  • 支持随机访问,查看元素较为简单
  • 添加、删除操作较为烦琐
底层源码
相关属性
public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
	//序列化ID
    private static final long serialVersionUID = 8683452581122892189L;
	//默认容量大小
    private static final int DEFAULT_CAPACITY = 10;
	//空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
	//默认容量大小的空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	//实际存放数据的数组,transient关键字修饰
    transient Object[] elementData; // non-private to simplify nested class access
	//元素个数
    private int size;
    
    //方法
}
构造方法
/**
 * 有參构造方法,判断传入的容量大小是否合法
 */
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        //传入容量为0,则为空数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

/**
 * 无参构造方法,默认容量为10
 */
public ArrayList() {
    //指向容量为0的默认空数组对象
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/**
 * 传入另一个集合对象
 */
public ArrayList(Collection<? extends E> c) {
    //数组对象指向传入集合对象的装换而来的数组
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // 如果传入的集合装换的数组元素类型不是Object类型,则转换为Object类型
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // 集合装换的数组没有元素,则替换成空数组
        this.elementData = EMPTY_ELEMENTDATA;
    }
}
添加操作
/**
 * 默认在数组尾部添加元素
 */
public boolean add(E e) {
    //检查是否需要扩容
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

/**
 * 在指定的位置插入新元素
 */
public void add(int index, E element) {
    //检查指定的插入位置是否合法,是否越界
    rangeCheckForAdd(index);
	//检查是否需要扩容
    ensureCapacityInternal(size + 1);
    //数组拷贝,将指定插入位置开始的元素往后移动一位
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    //在指定位置覆盖新元素
    elementData[index] = element;
    size++;
}

关于arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 方法
将原数组的指定一段拷贝到目标数组的指定位置

  • src 原数组
  • int 原数组开始拷贝的位置
  • dest 目标数组
  • destPos 目标数组接收拷贝的位置
  • length 拷贝的长度
扩容相关操作
/**
 *计算所需要的容量大小
 */
private void ensureCapacityInternal(int minCapacity) {
    //判断当前用的数组是否是默认容量大小的的数组
    //如果是,则计算当前需要的容量的大小
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

/**
 *判断是否需要扩容
 */
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // 如果需要的最小容量大于当前的数组容量
    // 即数组容量不够,则需要扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

/**
 *扩容操作
 */
private void grow(int minCapacity) {
    // 计算当前数组长度
    int oldCapacity = elementData.length;
    // 计算新数组数组长度,为原来的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    // 判断新的数的容量是否满足所需的容量大小
    if (newCapacity - minCapacity < 0)
        //如果不满足,则直接扩成所需的容量
        newCapacity = minCapacity;
    //不能一直无限扩容,当容量超过Integer.MAX_VALUE - 8时,直接扩成Integer.MAX_VALUE-8大小
    //最大为Integer.MAX_VALUE
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // 将原数组数据复制到扩容后的新数组中
    // Arrays.copyOf()代价很高,所以尽量在ArrayList创建时指定数组大小
    elementData = Arrays.copyOf(elementData, newCapacity);
}

//能分配的最大数组大小,再大可能会出现内存溢出异常
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

/**
 *当数组扩容超过一定门限,直接括成Integer.MAX_VALUE大小
 */
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
}

关于Arrays.copyOf(Object[] original,int newLength)方法
将指定数组的长度改变为newLength

  • original 原数组
  • newLength 新数组的长度,该长度可能小于原数组长度,所以该方法也可以缩容也可以扩容
删除元素
/**
 * 删除指定位置的元素
 */
public E remove(int index) {
    //检查下标是否大于size
    rangeCheck(index);

    modCount++;
    //记录要删除的值
    E oldValue = elementData(index);
	//计算要移动的长度
    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //将新空出来的位置设置为null,让GC可以回收这些空间
    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;
}
手动缩容
/**
 *手动缩容,将内部数组的容量降为size大小,如果size为0,则指向空数组
 *JDK内部没有调用该方法
 */
public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
            ? EMPTY_ELEMENTDATA
            : Arrays.copyOf(elementData, size);
    }
}
清空操作
public void clear() {
    modCount++;
	//将整个存放过数据的部分全部置null,让GC可以释放这些内存空间
    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;
    size = 0;
}
迭代器相关
/**
 *创建一个迭代器对象
 */
public Iterator<E> iterator() {
    return new Itr();
}

private class Itr implements Iterator<E> {
    //下一个元素对象的下标
    int cursor;       // index of next element to return
    //最后一个元素对象下标
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;

    public boolean hasNext() {
        return cursor != size;
    }

    /**
     *返回当前元素对象
     */
    @SuppressWarnings("unchecked")
    public E next() {
        //判断迭代器内元素是否为空
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    /**
     *删除当前元素
     */
    public void remove() {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}
总结
  • modCount字段记录内部结构发生修改的次数,元素添加、删除次数
  • 添加元素顺序:计算所需的最小容量(size+1)->判断是否需要扩容->进行扩容->添加元素
  • 扩容时进行判断,如果1.5倍的容量大小不能满足,则直接扩容成size+1大小,最大容量为Integer.MAX_VALUE
  • 提供了手动缩容方法,但内部没有调用(我没找到),将内部数组大小缩为size大小
  • 对于序列化这个问题,还不是很理解,所以就没有写出来了
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值