java集合进阶ArrayList源码

Posted 烟锁迷城

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java集合进阶ArrayList源码相关的知识,希望对你有一定的参考价值。

本篇文章多借鉴于b站账号:@河北王校长的直播,本人能力有限,阅读源码水平不足,此作为笔记以记录知识。

此文章分析的是ArrayList源码,最为常见的集合之一,希望能对读者有所帮助

目录

1、RandomAccess:随机存取

2、DEFAULT_CAPACITY:初始容量

3、elementData:存储数组

4、构造方法

4.1、初始化长度构造

 4.2、默认构造

 4.3、指定列表构造

5、其他创建方法 

5.1、Arrays.aslist:转化为固定长度的ArrayList

 5.2、Collections.emptyList():空EmptyList

6、trimToSize:切除至长度

7、ensureCapacity:增加容量

8、contains:是否存在

 9、indexOf:判断数据的数组位置

10、lastIndexOf:判断数据的数组位置(倒序查找)

 11、clone:克隆方法

 12、toArray:转换为新的数组

 13、get:获取elementData数组位置对应的元素

14、set:将数据放入指定下标位置

 15、add:添加方法

16、add:依照下标位置添加元素

 17、remove:移除某个下标位置的元素

18、remove:移除某个列表中的元素

19、clear:清除列表内全部数据

20、addAll:全部添加

21、addAll:全部添加自指定下标位置

22、removeRange:范围移除(仅限于同包,子包或继承类)

23、outOfBoundsMsg:打印信息,超出范围

24、removeAll:批量移除列表内的数值

25、writeObject:序列化写方法

26、readObject:序列化读方法

27、listIterator:获取某一个位置的迭代器

 28、listIterator:获取首位的迭代器

29、iterator:获取迭代器

30、subList:获取一个子列表

31、forEach:迭代方法,forEach循环,可以写lambda表达式

32、spliterator:并发遍历

33、removeIf:删除所有满足特定条件的数组元素

 34、replaceAll:全部替换

 35、sort:排序方法


1、RandomAccess:随机存取

进入ArrayList类,第一行代码就是介绍ArrayList继承了哪些类,实现了哪些接口。

AbstractList抽象列表类,List列表接口,Cloneable浅克隆接口,Serializable支持序列化接口,这些都是比较常用的接口,那么RandomAccess接口是做什么用的呢?

RandomAccess接口,从字面意思上来说,是随机存取,继承了此接口的类将会使用索引进行增删改查,这就意味着,对于实现RandomAccess接口的集合来说,for循环访问要比iterator迭代器速度更快

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

2、DEFAULT_CAPACITY:初始容量

 DEFAULT_CAPACITY,通常意义上来说,我们会认为这是ArrayList的默认初始容量,但事实的确如此吗?

实际上,只有在无参构造函数中,才会指定DEFAULTCAPACITY_EMPTY_ELEMENTDATA为初始化的空数组,只有使用DEFAULTCAPACITY_EMPTY_ELEMENTDATA为初始化空数组,add方法在进行扩容判定时,才会使用DEFAULT_CAPACITY作为初始化数组长度,其实由此可见,无参构造并不会赋予数组长度,这一动作直到add方法才真正完成。

EMPTY_ELEMENTDATA,空elementDate数组,是在指定长度为0时,ArrayList初始化使用的空数组,这样就无需创建新的数组。

DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,同样也是一个空数组,为什么会设置两个空数组?其实这个空数组是在无参构造函数和add方法中扩容使用的一个判定条件,无参构造使用的是DEFAULTCAPACITY_EMPTY_ELEMENTDATA 作为初始化的空数组。


​private static final int DEFAULT_CAPACITY = 10;

​
private static final Object[] EMPTY_ELEMENTDATA = ;


private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = ;

3、elementData:存储数组

 elementData数组是ArrayList存储数据的容器,因此ArrayList本质上就是一个数组。

在elementData前有一个关键字,transient。关键字transient的意义是禁止序列化,那么为什么ArrayList会在实现了Serializable接口的同时禁止其数据进行序列化呢?

因为整个ArrayList的数组空间是很难被全部占满的,其内部几乎一定会有部分空位置,那么在进行序列化时,会有空间的浪费,所以elementData禁止直接序列化。

那么序列化和反序列化之后ArrayList的数据会丢失吗?

当然不会,因为ArrayList重写了序列化方法,这一点会在以后提到

transient Object[] elementData;

4、构造方法

ArrayList包含三个构造方法

4.1、初始化长度构造

ArrayList允许指定初始长度。

当长度大于0,创建指定长度的数组

当长度等于0,使用EMPTY_ELEMENTDATA,也就是上面提到过的空数组来初始化

当长度小于0,抛出异常。

这种构造的意义在于,既然已知ArrayList长度,就直接进行精准的长度赋值,也就要避免对此ArrayList再进行add,remove等操作。

public ArrayList(int initialCapacity) 
    if (initialCapacity > 0) 
        this.elementData = new Object[initialCapacity];
     else if (initialCapacity == 0) 
        this.elementData = EMPTY_ELEMENTDATA;
     else 
        throw new IllegalArgumentException("Illegal Capacity: "+
                initialCapacity);
    

 4.2、默认构造

默认的构造器,在不指定长度时,使用DEFAULTCAPACITY_EMPTY_ELEMENTDATA作为初始化的空数组,这也意味着,初始化为空数组,无论如何,add时也会进行一次扩容

public ArrayList() 
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;

 4.3、指定列表构造

此处可以将一些实现了Collection接口的集合转化为list,存入其中。

public ArrayList(Collection<? extends E> c) 
    elementData = c.toArray();
    if ((size = elementData.length) != 0) 
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
     else 
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    

5、其他创建方法 

除了用构造方法之外,还有一些其他的生成ArrayList的有趣方法

5.1、Arrays.aslist:转化为固定长度的ArrayList

 Arrays.aslist()是一个很常见的创建ArrayList的方法,但是这个方法有点不同。

可以看到,这里new ArrayList<>(a)看上去好像和上面的有参构造一模一样,但实际上,这里调用的是Arrays内部的一个静态类,而在这个静态类里,并没有add方法!没有覆写父类的add方法,就会导致调用的就是AbstractList方法中的add,而这个父类的add会直接抛出异常。

所以,aslist方法通常使用在固定长度大小的ArrayList之中。

public static <T> List<T> asList(T... a) 
    return new ArrayList<>(a);


/**
 * @serial include
 */
private static class ArrayList<E> extends AbstractList<E>
    implements RandomAccess, java.io.Serializable

    private static final long serialVersionUID = -2764017481108945198L;
    private final E[] a;

    ArrayList(E[] array) 
        a = Objects.requireNonNull(array);
    

    @Override
    public int size() 
        return a.length;
    

    @Override
    public Object[] toArray() 
        return a.clone();
    

    @Override
    @SuppressWarnings("unchecked")
    public <T> T[] toArray(T[] a) 
        int size = size();
        if (a.length < size)
            return Arrays.copyOf(this.a, size,
                                 (Class<? extends T[]>) a.getClass());
        System.arraycopy(this.a, 0, a, 0, size);
        if (a.length > size)
            a[size] = null;
        return a;
    

    @Override
    public E get(int index) 
        return a[index];
    

    @Override
    public E set(int index, E element) 
        E oldValue = a[index];
        a[index] = element;
        return oldValue;
    

    @Override
    public int indexOf(Object o) 
        E[] a = this.a;
        if (o == null) 
            for (int i = 0; i < a.length; i++)
                if (a[i] == null)
                    return i;
         else 
            for (int i = 0; i < a.length; i++)
                if (o.equals(a[i]))
                    return i;
        
        return -1;
    

    @Override
    public boolean contains(Object o) 
        return indexOf(o) != -1;
    

    @Override
    public Spliterator<E> spliterator() 
        return Spliterators.spliterator(a, Spliterator.ORDERED);
    

    @Override
    public void forEach(Consumer<? super E> action) 
        Objects.requireNonNull(action);
        for (E e : a) 
            action.accept(e);
        
    

    @Override
    public void replaceAll(UnaryOperator<E> operator) 
        Objects.requireNonNull(operator);
        E[] a = this.a;
        for (int i = 0; i < a.length; i++) 
            a[i] = operator.apply(a[i]);
        
    

    @Override
    public void sort(Comparator<? super E> c) 
        Arrays.sort(a, c);
    

 5.2、Collections.emptyList():空EmptyList

Collections.emptyList()返回了一个EMPTY_LIST。

public static final List EMPTY_LIST = new EmptyList<>();

这个EmptyList是一个特殊的List,虽然继承了AbstractList,但实际上,这玩意根本不能添加,不能获取,是一个完全的空List。

因此,只有需要返回一个空列表的时候才使用这个方法,用以取代需要返回空列表时直接new ArrayList()来进行使用。

public static final <T> List<T> emptyList() 
    return (List<T>) EMPTY_LIST;


private static class EmptyList<E>
    extends AbstractList<E>
    implements RandomAccess, Serializable 
private static final long serialVersionUID = 8842843931221139166L;

public Iterator<E> iterator() 
    return emptyIterator();

public ListIterator<E> listIterator() 
    return emptyListIterator();


public int size() return 0;
public boolean isEmpty() return true;

public boolean contains(Object obj) return false;
public boolean containsAll(Collection<?> c)  return c.isEmpty(); 

public Object[] toArray()  return new Object[0]; 

public <T> T[] toArray(T[] a) 
    if (a.length > 0)
        a[0] = null;
    return a;


public E get(int index) 
    throw new IndexOutOfBoundsException("Index: "+index);


public boolean equals(Object o) 
    return (o instanceof List) && ((List<?>)o).isEmpty();


public int hashCode()  return 1; 

@Override
public boolean removeIf(Predicate<? super E> filter) 
    Objects.requireNonNull(filter);
    return false;

@Override
public void replaceAll(UnaryOperator<E> operator) 
    Objects.requireNonNull(operator);

@Override
public void sort(Comparator<? super E> c) 


// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> action) 
    Objects.requireNonNull(action);


@Override
public Spliterator<E> spliterator()  return Spliterators.emptySpliterator(); 

// Preserves singleton property
private Object readResolve() 
    return EMPTY_LIST;

6、trimToSize:切除至长度

trimToSize方法会将数组切除至其内部数据数量的长度

public void trimToSize() 
    modCount++;
    if (size < elementData.length) 
        elementData = (size == 0)
                ? EMPTY_ELEMENTDATA
                : Arrays.copyOf(elementData, size);
    

7、ensureCapacity:增加容量

ensureCapacity用于增加ArrayList容量,判断如果使用无参构造进行初始化则最小扩容量为10,否则为0,然后与目标容量做比较,如果不满足,执行扩容方法ensureExplicitCapacity,此方法将会在add方法中提及

public void ensureCapacity(int minCapacity) 
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
        // any size if not default element table
        ? 0
        // larger than default for default empty table. It's already
        // supposed to be at default size.
        : DEFAULT_CAPACITY;

    if (minCapacity > minExpand) 
        ensureExplicitCapacity(minCapacity);
    

8、contains:是否存在

 contains判断ArrayList是否存在某个数值,其中用到了indexOf方法

public boolean contains(Object o) 
    return indexOf(o) >= 0;

 9、indexOf:判断数据的数组位置

indexOf用来判断数组在数组的位置,用的就是最简单的for循环

public int indexOf(Object o) 
    if (o == null) 
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)
                return i;
     else 
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))
                return i;
    
    return -1;

10、lastIndexOf:判断数据的数组位置(倒序查找)

indexOf的倒序查找

public int lastIndexOf(Object o) 
    if (o == null) 
        for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
     else 
        for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    
    return -1;

 11、clone:克隆方法

clone方法是浅拷贝,即拷贝出来的数据的地址依旧指向原本的数据,详细可以查看设计模式中的原型模式

可以看到,用clone创造出来的ArrayList在modCount的值上都是0,可以视作从未做过任何增改删操作。

public Object clone() 
    try 
        ArrayList<?> v = (ArrayList<?>) super.clone();
        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(e);
    

 12、toArray:转换为新的数组

toArray直接调用了Arrays.copyof方法来进行数组复制

public Object[] toArray() 
    return Arrays.copyOf(elementData, size);

在泛型条件下,可以指定将ArrayList数组内的数据移动至一个指定的数组。

如果数组小于ArrayList元素数量,直接调用Arrays.copyof(),否则执行System.arraycopy(),在复制数据方面,System.arraycopy()是效率很高的复制方法,如果想要复制数组,可以考虑直接使用这个方法。

数组的长度大于想要复制的ArrayList的数据长度,将a[size]=null,主要是为了垃圾回收

在JVM之中,数组不是对象,而是对象元素的合集,所以数组里的元素是对象,之所以要把最后一个对象置为空,是因为JVM在进行垃圾回收的时候,会进行可达性分析,那么一个NULL对象是不可达的,这样就可以触发垃圾回收

public <T> T[] toArray(T[] a) 
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;

 13、get:获取elementData数组位置对应的元素

rangeCheck方法先进行下标检查,是否已经超过

public E get(int index) 
    rangeCheck(index);

    return elementData(index);

 如果请求的下标数超过长度,则抛出错误

private void rangeCheck(int index) 
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

14、set:将数据放入指定下标位置

rangeCheck方法先进行下标检查,是否已经超过。

然后将新值与旧值做交换,返回旧数值

public E set(int index, E element) 
    rangeCheck(index);

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

 15、add:添加方法

add是最最常用的方法,添加,除了在数组中添加一个元素之外,这个方法还关乎到ArrayList的扩容和最大容量问题。

无论是elementData数组添加新元素还是返回true,显然都是非常简单的,那么ensureCapacityInternal方法显然就是关键了。

因为ensureCapacityInternal方法就是起到扩容和检验长度是否超过最大值作用的方法

public boolean add(E e) 
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;

ensureCapacityInternal内包含两个方法,ensureExplicitCapacity和calculateCapacity,我们一一分析

private void ensureCapacityInternal(int minCapacity) 
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));


 calculateCapacity方法第一行语句就是进行一个判断

if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)

DEFAULTCAPACITY_EMPTY_ELEMENTDATA,一个空的数组,如果当前数组是空数组,那么就返回默认长度和当前最小需要长度中比较大的那个。

还记得什么时候才会指定DEFAULTCAPACITY_EMPTY_ELEMENTDATA作为初始化的数组吗?是的,只有无参构造,才会使用它,才会让长度为默认长度DEFAULT_CAPACITY,这下我们一下解决了两个问题。

总结:只有使用无参构造的时候,elementData数组才会被初始化为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,这时数组的初始长度才为DEFAULT_CAPACITY,也就是10

除此之外,可以看到在使用无参构造创建ArrayList的情况下,数组的长度直到add方法被调用才被真正赋予

private static int calculateCapacity(Object[] elementData, int minCapacity) 
    //如果当前数组等于默认长度的空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) 
        返回当前长度与默认长度中较大的那个
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    
    //返回当前计算长度
    return minCapacity;

 modcount++,一个自增的数字类型,这是做什么用的呢?

这涉及到ArrayList的快速失败机制,在并发访问的情况下,为了防止ArrayList数据被其他线程篡改,每一次数据的操作都将会被进行一次计数,如果预期修改计数与实际修改计数不符,就证明数据被篡改,此时将直接抛出失败

接下来是判断当前需要的最小长度与数组长度的比较,如果大于,则需要进行扩容

private void ensureExplicitCapacity(int minCapacity) 
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);

 列出旧长度和新长度,新长度为旧长度+旧长度右移一位,右移一位实际上就是长度减半,也就是通常意义下的1.5倍扩容

得到新长度之后,会进行比较,如果仍不能满足最小长度需要,则新长度直接等于最小需要长度。

得到真正需要的长度之后,会与ArrayList允许的最大长度MAX_ARRAY_SIZE 进行比较,如果已经比MAX_ARRAY_SIZE 还大,就会执行hugeCapacity方法

最后,将旧数组数据复制到新数组之中,完成扩容

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
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);

 接下来讨论的是hugeCapacity方法

第一个判断如果最小需要长度小于1,触发OOM

下一个判断,比较的是最小需要长度和Integer.MAX_VALUE - 8的大小,如果大于,返回Integer最大值,否则返回Integer.MAX_VALUE - 8,这个MAX_ARRAY_SIZE的Integer.MAX_VALUE - 8是怎么来的呢?

这关系到数组在JVM中的保存方式,因为数组比较特殊,不是引用类型也不是基本类型,而是用元素类型拼出来的一个类型,所以需要有位置保存其长度,这个位置就在对象头里,所以-8是为了保存数组length长度

总结:ArrayList最大理论长度是2^32-1

private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private static int hugeCapacity(int minCapacity) 
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;

复制的Array.copy方法是最耗时的方法,因为会调用System.arraycopy方法

public static <T> T[] copyOf(T[] original, int newLength) 
    return (T[]) copyOf(original, newLength, original.getClass());

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) 
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;

16、add:依照下标位置添加元素

可以看到,第一个方法是rangeCheckForAdd,依旧是检查范围,但是是添加的检查范围

ensureCapacityInternal方法,上面的add方法并无区别。

System.arraycopy,将index后面的数据向后移动

将数值放在指定的数组下标位置

长度自增1

public void add(int index, E element) 
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;

 检查下标是否小于0或者超出长度

private void rangeCheckForAdd(int index) 
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

 17、remove:移除某个下标位置的元素

rangeCheck检查范围

modcount++,快速失败机制

取出旧数值

如果移除的元素不在数组末尾,则将数组从index位置以后的数据向前移动一位

将ArrayList长度最后一位的数组值置为null,为了垃圾回收。

返回被移除的元素

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;

18、remove:移除某个列表中的元素

使用两个full循环来查找需要移除的元素

在移除的时候调用了一个fastRemove方法

返回值是Boolean

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;

这个fastRemove其实也没什么特别的,只是把上一个remove里的部分语句放进来了。

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

19、clear:清除列表内全部数据

操作数modCount自增

循环清除,可以看到,这里的数组长度没有被改变,也就是说,ArrayList容量没有改变,只是被去除了内容

public void clear() 
    modCount++;

    // clear to let GC do its work
    for (int i = 0; i < size; i++)
        elementData[i] = null;

    size = 0;

20、addAll:全部添加

addAll和add方法很类似,依旧采用了扩容和复制

要注意的是返回值,addAll的返回值有关于传入集合的长度,如果传入集合长度为0,则返回false

public boolean addAll(Collection<? extends E> c) 
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;

21、addAll:全部添加自指定下标位置

addAll和上一个addAll很相似,只不过多出来一个插入到指定位置,所以依旧会出现数组数据的移动,而且在index不在尾部插入的时候会移动两次。

返回结果依旧是如果传入集合长度为0,则返回false

public boolean addAll(int index, Collection<? extends E> c) 
    rangeCheckForAdd(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount

    int numMoved = size - index;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);

    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;

22、removeRange:范围移除(仅限于同包,子包或继承类)

操作计数modCount自增

计算出要移动的数据长度

将数组的toIndex位置到结尾的数据移动到fromIndex之后

计算出需要清除的下标位置

将数组数据置为NULL,便于垃圾回收

ArrayList长度充值

protected void removeRange(int fromIndex, int toIndex) 
    modCount++;
    int numMoved = size - toIndex;
    System.arraycopy(elementData, toIndex, elementData, fromIndex,
                     numMoved);

    // clear to let GC do its work
    int newSize = size - (toIndex-fromIndex);
    for (int i = newSize; i < size; i++) 
        elementData[i] = null;
    
    size = newSize;

23、outOfBoundsMsg:打印信息,超出范围

这里提一个比较有趣的点,在较少的几个String类型拼接的情况下,直接使用+即可,之后大量的字符串拼接才适合使用StringBuilder或StringBuffer,因为创建对象也是需要很大性能的。

private String outOfBoundsMsg(int index) 
    return "Index: "+index+", Size: "+size;

24、removeAll:批量移除列表内的数值

先判断了一个是否为空

然后执行了批量移除

public boolean removeAll(Collection<?> c) 
    Objects.requireNonNull(c);
    return batchRemove(c, false);

这个判空方法是非空,只要不是空就行,所以参数可以传一个空集合,但不能传NULL

public static <T> T requireNonNull(T obj) 
    if (obj == null)
        throw new NullPointerException();
    return obj;

在这里,有两个下标,r和w。

在try代码块里,有一个很有趣的操作,如果当前遍历得到的数组值不在移除数组值内,则r和w同步自增,数值也被一并赋予。一旦存在,则不再执行,这也就意味着,r的下标会移动,但是w的不会,这就让r的数值移动到w的少了一个,这就起到了移除的作用

finally代码块里,如果r的长度比size小,就要把r至size的数据移动至w之后,重新计算w数值

将w之后的数组位置空,此处modCount的计算方式是移动一个就自增一次

private boolean batchRemove(Collection<?> c, boolean complement) 
    final Object[] elementData = this.elementData;
    int r = 0, w = 0;
    boolean modified = false;
    try 
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
     finally 
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        if (r != size) 
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        
        if (w != size) 
            // clear to let GC do its work
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        
    
    return modified;

25、writeObject:序列化写方法

writeObject是一种用于序列化写的方法,首先,将modCount赋予expectedModCount,原因有两个,第一个是modCount本身也是transient修饰的,不能序列化

protected transient int modCount = 0;

另一方面是业务可能在并发条件下修改列表数值,导致序列化失败,但是业务代码优先级高于序列化,所以需要重新序列化

然后循环,将数组数值按照列表大小进行写入,这样就保证没有多余的空间被浪费。

private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) 
        s.writeObject(elementData[i]);
    

    if (modCount != expectedModCount) 
        throw new ConcurrentModificationException();
    

26、readObject:序列化读方法

readObject是一种用于序列化读的方法

将空数组作为初始化的载体,避免创建新的对象。

如果获取到的序列化长度不大于0,证明本身就是空的,无需继续关心。

如果大于0,就按照长度将数据重新循环读回。

private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException 
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // Read in capacity
    s.readInt(); // ignored

    if (size > 0) 
        // be like clone(), allocate array based upon size not capacity
        int capacity = calculateCapacity(elementData, size);
        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) 
            a[i] = s.readObject();
        
    

27、listIterator:获取某一个位置的迭代器

这段代码非常简单,就是先判定所获取的位置会不会超限,然后创建一个迭代器

public ListIterator<E> listIterator(int index) 
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index);
    return new ListItr(index);

ListItr是一个私有的类,继承了Itr,实现了ListIteration,构造使用父类的构造器,并且置入index

hasPrevious,具有前一个节点,只要不是0,就具有

nextIndex,当前节点索引

previousIndex,上一节点索引

previous,前一个,checkForComodification方法,快速失败检测,然后获取前一个索引号,遍历数组,获得需要的数据,其中lastRet是上一次遍历时返回的索引位置,所以需要更新

set,放入,放入的位置是lastRet

add,将要添加的数据添加到此迭代器所在的位置,调用的依旧是ArrayList的add,注意,这里的add方法会重置lastRet和modCount计数,并且让当前索引计数前进1。

private class ListItr extends Itr implements ListIterator<E> 
    ListItr(int index) 
        super();
        cursor = index;
    

    public boolean hasPrevious() 
        return cursor != 0;
    

    public int nextIndex() 
        return cursor;
    

    public int previousIndex() 
        return cursor - 1;
    

    @SuppressWarnings("unchecked")
    public E previous() 
        checkForComodification();
        int i = cursor - 1;
        if (i < 0)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i;
        return (E) elementData[lastRet = i];
    

    public void set(E e) 
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try 
            ArrayList.this.set(lastRet, e);
         catch (IndexOutOfBoundsException ex) 
            throw new ConcurrentModificationException();
        
    

    public void add(E e) 
        checkForComodification();

        try 
            int i = cursor;
            ArrayList.this.add(i, e);
            cursor = i + 1;
            lastRet = -1;
            expectedModCount = modCount;
         catch (IndexOutOfBoundsException ex) 
            throw new ConcurrentModificationException();
        
    

比较modCount的方法,不再赘述

final void checkForComodification() 
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();

 28、listIterator:获取首位的迭代器

与上一个方法类似,此处不再赘述

public ListIterator<E> listIterator() 
    return new ListItr(0);

29、iterator:获取迭代器

可以看到,这里的方法就是获取一个Itr类

public Iterator<E> iterator() 
    return new Itr();

Itr实现了Iterator

hasNext:是否已经到达size的长度,很简单,单纯比较长度

next:检测modCount,将cursor的数值赋予i,校验长度,校验数组长度,将cursor计数加1,推进至下一个数组位,返回i对应的数组数据,同时赋予lastRet返回的i数组位

remove:移除lastRet数组位的数据,将lastRet的数据位赋予cursor,然后将lastRet和modcount都赋予初始数值

forEachRemaining:取出迭代器剩余的数据,其剩余数据的标志是当前迭代器所具有的索引位置,即此方法会将cursor的位置开始,size-1的位置结束,并且会将lastRet的数据置为size-1,因此一旦执行这个方法,迭代器中将不再拥有数据,这就是取出所有剩余数据的含义。

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;

    Itr() 

    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();
        
    

    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) 
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) 
            return;
        
        final Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) 
            throw new ConcurrentModificationException();
        
        while (i != size && modCount == expectedModCount) 
            consumer.accept((E) elementData[i++]);
        
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    

    final void checkForComodification() 
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    

30、subList:获取一个子列表

subList,一个子列表,这是一个单独实现的私有类,数值范围是输入的起始位置和终止位置。subList具有一切ArrayList的方法和迭代器,可以仔细的看一下。

public List<E> subList(int fromIndex, int toIndex) 
    subListRangeCheck(fromIndex, toIndex, size);
    return new SubList(this, 0, fromIndex, toIndex);

边界检测,衡量起始位置和终止位置是否超限,起始位置是否大于终止位置

static void subListRangeCheck(int fromIndex, int toIndex, int size) 
    if (fromIndex < 0)
        throw new IndexOutOfBoundsException("fromIndex = " + fromIndex);
    if (toIndex > size)
        throw new IndexOutOfBoundsException("toIndex = " + toIndex);
    if (fromIndex > toIndex)
        throw new IllegalArgumentException("fromIndex(" + fromIndex +
                                           ") > toIndex(" + toIndex + ")");

SubList是一个继承AbstractList的类,这个类同时实现RandomAccess接口,证明需要无序存取。

其构造方法需要AbstractList,offset偏移量,起始位置和终止位置,分别用来赋予一些初始值。

set:将数据放在指定数据位,进行长度检测和modCount检测,其真正的添加位置是index(目标位置)+offset(偏移量),将新数值替换旧数值,并返回旧数值

get:获取指定位置的数据,进行长度检测和modCount检测,其真正的获取位置是index(目标位置)+offset(偏移量)

size:获取长度

add:获取指定位置的数据,进行长度检测和modCount检测,其真正的添加位置是index(目标位置)+offset(偏移量)

remove:移除指定位置的数据,进行长度检测和modCount检测,其真正的移除位置是index(目标位置)+offset(偏移量)

private class SubList extends AbstractList<E> implements RandomAccess 
    private final AbstractList<E> parent;
    private final int parentOffset;
    private final int offset;
    int size;

    SubList(AbstractList<E> parent,
            int offset, int fromIndex, int toIndex) 
        this.parent = parent;
        this.parentOffset = fromIndex;
        this.offset = offset + fromIndex;
        this.size = toIndex - fromIndex;
        this.modCount = ArrayList.this.modCount;
    

    public E set(int index, E e) 
        rangeCheck(index);
        checkForComodification();
        E oldValue = ArrayList.this.elementData(offset + index);
        ArrayList.this.elementData[offset + index] = e;
        return oldValue;
    

    public E get(int index) 
        rangeCheck(index);
        checkForComodification();
        return ArrayList.this.elementData(offset + index);
    

    public int size() 
        checkForComodification();
        return this.size;
    

    public void add(int index, E e) 
        rangeCheckForAdd(index);
        checkForComodification();
        parent.add(parentOffset + index, e);
        this.modCount = parent.modCount;
        this.size++;
    

    public E remove(int index) 
        rangeCheck(index);
        checkForComodification();
        E result = parent.remove(parentOffset + index);
        this.modCount = parent.modCount;
        this.size--;
        return result;
    

    protected void removeRange(int fromIndex, int toIndex) 
        checkForComodification();
        parent.removeRange(parentOffset + fromIndex,
                           parentOffset + toIndex);
        this.modCount = parent.modCount;
        this.size -= toIndex - fromIndex;
    

    public boolean addAll(Collection<? extends E> c) 
        return addAll(this.size, c);
    

    public boolean addAll(int index, Collection<? extends E> c) 
        rangeCheckForAdd(index);
        int cSize = c.size();
        if (cSize==0)
            return false;

        checkForComodification();
        parent.addAll(parentOffset + index, c);
        this.modCount = parent.modCount;
        this.size += cSize;
        return true;
    

    public Iterator<E> iterator() 
        return listIterator();
    

    public ListIterator<E> listIterator(final int index) 
        checkForComodification();
        rangeCheckForAdd(index);
        final int offset = this.offset;

        return new ListIterator<E>() 
            int cursor = index;
            int lastRet = -1;
            int expectedModCount = ArrayList.this.modCount;

            public boolean hasNext() 
                return cursor != SubList.this.size;
            

            @SuppressWarnings("unchecked")
            public E next() 
                checkForComodification();
                int i = cursor;
                if (i >= SubList.this.size)
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (offset + i >= elementData.length)
                    throw new ConcurrentModificationException();
                cursor = i + 1;
                return (E) elementData[offset + (lastRet = i)];
            

            public boolean hasPrevious() 
                return cursor != 0;
            

            @SuppressWarnings("unchecked")
            public E previous() 
                checkForComodification();
                int i = cursor - 1;
                if (i < 0)
                    throw new NoSuchElementException();
                Object[] elementData = ArrayList.this.elementData;
                if (offset + i >= elementData.length)
                    throw new ConcurrentModificationException();
                cursor = i;
                return (E) elementData[offset + (lastRet = i)];
            

            @SuppressWarnings("unchecked")
            public void forEachRemaining(Consumer<? super E> consumer) 
                Objects.requireNonNull(consumer);
                final int size = SubList.this.size;
                int i = cursor;
                if (i >= size) 
                    return;
                
                final Object[] elementData = ArrayList.this.elementData;
                if (offset + i >= elementData.length) 
                    throw new ConcurrentModificationException();
                
                while (i != size && modCount == expectedModCount) 
                    consumer.accept((E) elementData[offset + (i++)]);
                
                // update once at end of iteration to reduce heap write traffic
                lastRet = cursor = i;
                checkForComodification();
            

            public int nextIndex() 
                return cursor;
            

            public int previousIndex() 
                return cursor - 1;
            

            public void remove() 
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();

                try 
                    SubList.this.remove(lastRet);
                    cursor = lastRet;
                    lastRet = -1;
                    expectedModCount = ArrayList.this.modCount;
                 catch (IndexOutOfBoundsException ex) 
                    throw new ConcurrentModificationException();
                
            

            public void set(E e) 
                if (lastRet < 0)
                    throw new IllegalStateException();
                checkForComodification();

                try 
                    ArrayList.this.set(offset + lastRet, e);
                 catch (IndexOutOfBoundsException ex) 
                    throw new ConcurrentModificationException();
                
            

            public void add(E e) 
                checkForComodification();

                try 
                    int i = curs

以上是关于java集合进阶ArrayList源码的主要内容,如果未能解决你的问题,请参考以下文章

Java小白集合源码的学习系列:ArrayList

Java小白集合源码的学习系列:ArrayList

java集合系列之ArrayList源码分析

JAVA集合之ArrayList源码分析

Java集合源码学习笔记ArrayList分析

JAVA——底层源码阅读——集合ArrayList的实现底层源码分析