ArrayList源码分析

本文分析的源码版本是1.7
ArrayList继承了AbstractList并实现了List,RandomAccess, Cloneable, java.io.Serializable 接口,
AbstractList提供了List接口的默认实现(个别方法为抽象方法)。
List接口定义了列表必须实现的方法。
RandomAccess是一个标记接口,接口内没有定义任何内容。
实现了Cloneable接口的类,可以调用Object.clone方法返回该对象的浅拷贝。
通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。序列化接口没有方法或字段,仅用于标识可序列化的语义。

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

因为没有final修饰,所以可以被继承。

属性

private static final long serialVersionUID = 8683452581122892189L;
//默认容量
private static final int DEFAULT_CAPACITY = 10;

//空数组,当用户调用无参构造函数时,elementData等于该数组 
private static final Object[] EMPTY_ELEMENTDATA = {};

//ArrayList基于该数组实现,用该数组保存数据, ArrayList 的容量就是该数组的长度,该数组等于EMPTY_ELEMENTDATA时,当第一次添加元素进入 ArrayList 中时,数组将扩容值 DEFAULT_CAPACITY(10) 
private transient Object[] elementData;

// ArrayList实际存储的数据数量
private int size;
//数组缓冲区最大存储容量,一些 VM 会在一个数组中存储某些数据(为什么要减去 8 的原因);尝试分配这个最大存储容量,可能会导致 OutOfMemoryError(当该值 > VM 的限制时) 
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造函数:

//创建一个初试容量为initialCapacity的、空的ArrayList,当初试容量值非法(小于0)时抛出IllegalArgumentException。
public ArrayList(int initialCapacity)

//创建一个空的ArrayList,此时其内数组缓冲区elementData=EMPTY_ELEMENTDATA={}, 容量为 0,当元素第一次被加入时,扩容至默认容量10 
public ArrayList()

//创建一个包含collection的ArrayList。
public ArrayList(Collection<? extends E> c) {
    //集合转 Object[] 数组
    elementData = c.toArray();
    size = elementData.length;
// c.toArray 可能不会返回 Object[],可以查看 java 官方编号为 6260652 的 bug  
    if(elementData.getClass() != Object[].class)
    // 若 c.toArray() 返回的数组类型不是 Object[],则利用 Arrays.copyOf(); 来构造一个大小为 size 的 Object[] 数组
        elementData = Arrays.copyOf(elementData, size, Object[].class);
}

容量调整方法:

// 将数组缓冲区大小调整到实际 ArrayList 存储元素的大小,该方法由用户手动调用,以减少空间资源浪费的目的 如:当实际大小 < 数组缓冲区大小时,如调用默认构造函数后,刚添加一个元素,此时 elementData.length = 10,而 size = 1,通过这一步,可以使得空间得到有效利用,而不会出现资源浪费的情况  
public void trimToSize() 
{
    //modCount是fail fast机制,在jdk1.7中去掉了volatile修饰,因为感觉没有必要为非线程安全集合浪费效率
  modCount++;
  if (size < elementData.length) {
      elementData = Arrays.copyOf(elementData, size);
    }
}

扩容分为两种:为了性能调优的手动扩容ensureCapacity()和add()方法中容量不够时的自动扩容方法ensureCapacityInternal()。代码如下:

public void ensureCapacity(int minCapacity) {
//其实就是判断是否需要扩容。首先判断现有的ArrayList容量是否为0,如果不是则选择最小扩充容量为0,如果是0,则选择最小扩充容量为10;同时再次判断人工填写的扩充容量是否比最小扩充容量大,是则进行扩容,不是则不扩容。
    int minExpand = (elementData != EMPTY_ELEMENTDATA) ? 0:DEFAULT_CAPACITY;
    if (minCapacity > minExpand) {
        ensureExplicitCapacity(minCapacity);
    }
}

private void ensureCapacityInternal(int minCapacity) {
//此方法在add()方法中被调用,其中minCapacity = size+1。首先确定ArrayList自动扩容的容量是多少。如果现有的容量是0,则选择默认的容量10进行扩容,如果现有的容量不是0,则选择现有容量+1的容量进行扩容。
   if (elementData == 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;
    //这是系统提供的扩容容量,即现有的容量+现有容量的/2;注意在单线程或多线程下newCapacity都有可能会溢出,变为负数。
    int newCapacity = oldCapacity + (oldCapacity>>1);
    //以下四行代码有两个作用,一是判断系统提供的扩充容量和minCapacity进行比较,谁大而且没有超过最大内存限制,就选谁。二是对newCapacity溢出进行处理。
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    elementData = Arrays.copyOf(elementData, newCapacity);
}
//超出ArrayList最大容量值时的处理
private static int hugeCapacity(int minCapacity) 
{
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE : MAX_ARRAY_SIZE;
}

其他方法

(1)深度复制

//深度复制:对拷贝出来的 ArrayList 对象的操作,不会影响原来的 ArrayList,返回一个克隆的 ArrayList 实例(深度复制的结果) 
public Object clone() {
    try {
        // Object 的克隆方法:会复制本对象及其内所有基本类型成员和 String 类型成员,但不会复制对象成员、引用对象
        @SuppressWarnings("unchecked")
        ArrayList<E> v = (ArrayList<E>)super.clone();
       // 对需要进行复制的引用变量,进行独立的拷贝:将存储的元素移入新的 ArrayList 中  
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError();
    }
}

转为Object数组的操作:

//返回 ArrayList 的 Object 数组,包含 ArrayList 的所有储存元素,对返回的该数组进行操作,不会影响该 ArrayList(相当于分配了一个新的数组)==>该操作是安全的,元素存储顺序与 ArrayList 中的一致 
public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}


//返回 ArrayList 元素组成的数组,若a的长度小于集合的长度,则重新建立一个T[]类型的数组,并将集合中的元素按序复制到其中,若数组a的大小 >= ArrayList的元素个数,则将ArrayList的全部元素都拷贝到数组a中。
//注意:若 a 中本来存储有元素,则 a 会被 list 的元素覆盖,且 a[list.size] = null,而a[list.size + 1] 及其后的元素依旧是 a 的元素。
public <T> T[] toArray(T[] a) {
    if (a.length < size)
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

判断是否集合中是否包含某一对象操作:

//判断该 ArrayList 是否包含指定对象(Object 类型,面对抽象编程,向上转型是安全的),
public boolean contains(Object o) 
//顺序查找,返回元素的最低索引值(最首先出现的索引位置) 
public int indexOf(Object o) 

//反向查找(从数组末尾向开始查找),返回元素的最高索引值
public int lastIndexOf(Object o)

获取或改变集合中单个元素值的操作:

//返回在索引为 index 的元素:数组的随机访问。默认包访问权限,封装粒度很强,连数组随机取值都封装为一个方法。主要是避免每次取值都要强转===>设置值就没有封装成一个方法,因为设置值不需要强转 
E elementData(int index)

//获取指定位置的元素:从 0 开始计数
public E get(int index)

//设置 index 位置元素的值,返回替换前的元素值。
public E set(int index, E element)

向集合中增加元素操作:

//添加新值到 list 末尾
public boolean add(E e)

//在指定位置增加新元素,原先在 index 位置的值往后移动一位
public void add(int index, E element)

//在集合的末尾增加集合c中的全部元素
public boolean addAll(Collection<? extends E> c)

//从集合的指定位置开始,增加c集合的全部元素,集合中指定位置之后的元素全部后移。
public boolean addAll(int index, Collection<? extends E> c)

删除集合中的元素操作:

//移除指定索引位置的元素:index 之后的所有元素依次左移一位
public E remove(int index)

//删除 ArrayList 中的一个指定元素(符合条件的索引最低).
public boolean remove(Object o)

//私有方法:快速删除第 index 个元素,该方法会跳过越界检查 
private void fastRemove(int index)

//它会将数组缓冲区所以元素置为 null,清空后,我们直接打印 list,却只会看见一个 []
public void clear()

//删除集合中指定范围内的元素,范围是[fromIndex,toIndex). 
protected void removeRange(int fromIndex, int toIndex)
//移除 list 中和 c 中共有的元素
public boolean removeAll(Collection<?> c)

//只保留 list 和 集合 c 中公有的元素
public boolean retainAll(Collection<?> c)
//批量删除
private boolean batchRemove(Collection<?> c, boolean complement)

手工序列化和反序列化:
elementData数组的前面有一个关键字transient,它是用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。所以不可以对elementData进行自动序列化,只能进行手工序列化。

//序列化
private void writeObject(ObjectOutputStream s) throws IOException

//反序列化
private void readObject(java.io.ObjectInputStream s)
 throws java.io.IOException, ClassNotFoundException

迭代器:
能向前和向后进行偏离的迭代器:

//返回一个从指定位置开始的ListItr迭代器实例
public ListIterator<E> listIterator(int index)

//返回一个从0开始的ListItr迭代器实例
public ListIterator<E> listIterator()
//可进行双向移动的迭代器类,可以向集合中增加元素
//注意:获取前一个元素的索引时,是 cursor 前一个,而不是当前元素前一个的索引。若调用 next()后马上调用该方法,则返回的是next()返回元素的索引。add()和remove()不能连续调用。
private class ListItr extends Itr implements ListIterator<E> {}

只能向后遍历的迭代器:

//返回一个 Itr 迭代器实例
public Iterator<E> iterator()
//只能向后遍历的迭代器类,没有add()方法,remove()也不能连续调用
private class Itr implements Iterator<E> {}

sublist()方法:
注意:获取从 fromIndex 到 toIndex 之间的子集合(左闭右开区间)若 fromIndex == toIndex,则返回的空集合,对该子集合的操作,会影响原有集合,当调用了 subList() 后,若对原有集合进行添加或删除操作时,会抛出异常 java.util.ConcurrentModificationException 。该子集合支持所有的集合操作

//其返回一个SubList类的实例
public List<E> subList(int fromIndex, int toIndex)

private class SubList extends AbstractList<E> implements RandomAccess

注意这里有个小知识点:
对 ArrayList 进行操作(如 add()、clear())后,即使 elementData 实际上的长度 > 其内元素个数,但是再直接打印该list时,显示结果还是[x,x,x] (注:x 代表其内实际(或有效)存储的元素)
对于 elementData 中存储情况和直接打印list (即System.out.println(list))结果不一致的原因,是因为 AbstractCollection 中对 toString() 进行了定义。代码如下:

public String toString() {
 Iterator<E> it = iterator();
    if (! it.hasNext())
        return "[]";

    StringBuilder sb = new StringBuilder();
    sb.append('[');
    for (;;) {
        E e = it.next();
        sb.append(e == this ? "(this Collection)" : e);
        if (! it.hasNext())
            return sb.append(']').toString();
        sb.append(',').append(' ');
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值