java集合进阶(一)ArrayList源码

java集合进阶(一)ArrayList源码,第1张

本篇文章多借鉴于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 extends AbstractList
        implements List, 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 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  List asList(T... a) {
    return new ArrayList<>(a);
}

/**
 * @serial include
 */
private static class ArrayList extends AbstractList
    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[] toArray(T[] a) {
        int size = size();
        if (a.length < size)
            return Arrays.copyOf(this.a, size,
                                 (Class) 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 spliterator() {
        return Spliterators.spliterator(a, Spliterator.ORDERED);
    }

    @Override
    public void forEach(Consumer action) {
        Objects.requireNonNull(action);
        for (E e : a) {
            action.accept(e);
        }
    }

    @Override
    public void replaceAll(UnaryOperator 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 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  List emptyList() {
    return (List) EMPTY_LIST;
}

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

public Iterator iterator() {
    return emptyIterator();
}
public ListIterator 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[] 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 filter) {
    Objects.requireNonNull(filter);
    return false;
}
@Override
public void replaceAll(UnaryOperator operator) {
    Objects.requireNonNull(operator);
}
@Override
public void sort(Comparator c) {
}

// Override default methods in Collection
@Override
public void forEach(Consumer action) {
    Objects.requireNonNull(action);
}

@Override
public Spliterator 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[] 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[] copyOf(T[] original, int newLength) {
    return (T[]) copyOf(original, newLength, original.getClass());
}
public static  T[] copyOf(U[] original, int newLength, Class 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 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 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 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
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
27、listIterator:获取某一个位置的迭代器

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

public ListIterator 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 {
    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 listIterator() {
    return new ListItr(0);
}
29、iterator:获取迭代器

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

public Iterator 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 {
    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 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 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 implements RandomAccess {
    private final AbstractList parent;
    private final int parentOffset;
    private final int offset;
    int size;

    SubList(AbstractList 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 c) {
        return addAll(this.size, c);
    }

    public boolean addAll(int index, Collection 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 iterator() {
        return listIterator();
    }

    public ListIterator listIterator(final int index) {
        checkForComodification();
        rangeCheckForAdd(index);
        final int offset = this.offset;

        return new ListIterator() {
            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 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 = cursor;
                    SubList.this.add(i, e);
                    cursor = i + 1;
                    lastRet = -1;
                    expectedModCount = ArrayList.this.modCount;
                } catch (IndexOutOfBoundsException ex) {
                    throw new ConcurrentModificationException();
                }
            }

            final void checkForComodification() {
                if (expectedModCount != ArrayList.this.modCount)
                    throw new ConcurrentModificationException();
            }
        };
    }

    public List subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, offset, fromIndex, toIndex);
    }

    private void rangeCheck(int index) {
        if (index < 0 || index >= this.size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

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

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

    private void checkForComodification() {
        if (ArrayList.this.modCount != this.modCount)
            throw new ConcurrentModificationException();
    }

    public Spliterator spliterator() {
        checkForComodification();
        return new ArrayListSpliterator(ArrayList.this, offset,
                                           offset + this.size, this.modCount);
    }
}
31、forEach:迭代方法,forEach循环,可以写lambda表达式

其内部是一个通过for循环实现的方法,在循环内部使用了lambda表达式的语义,简化 *** 作

public void forEach(Consumer action) {
    Objects.requireNonNull(action);
    final int expectedModCount = modCount;
    @SuppressWarnings("unchecked")
    final E[] elementData = (E[]) this.elementData;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        action.accept(elementData[i]);
    }
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
}
32、spliterator:并发遍历

spliterator是java8的新特性,是集合中的遍历方法,但这个遍历与迭代器不同的是,它是并发遍历的。

可以看到,这里返回的是一个指定的ArrayListSpliterator,其初始值分别是this,0,-1,0,这些值代表着本身要遍历的ArrayList,初始下标,最终下标和预期修改数量(快速失败使用)

getFence:获取到能遍历的最后一个位置,不必赘述,很简单。

trySplit:尝试分区,这个方法会使用当前的index与能取到的最后一个位置相加,然后右移一位,即取半,如果此数值大于当前索引,即将当前索引至计算数值之间的数据取出成为新的ArrayListSpliterator

tryAdvance:依照lambda表达式来对数据进行处理,处理的数据是当前index的下一位,如果返回true证明还有数据未处理,返回false则表明已经没有剩余数据可以处理

forEachRemaining:遍历剩余的数据,这个函数很类似于迭代器Iteration的同名函数,都是取出剩余的数据进行循环。

estimateSize:获取剩余长度

characteristics:获取特征数

public Spliterator spliterator() {
    return new ArrayListSpliterator<>(this, 0, -1, 0);
}

static final class ArrayListSpliterator implements Spliterator {

    private final ArrayList list;
    private int index; // current index, modified on advance/split
    private int fence; // -1 until used; then one past last index
    private int expectedModCount; // initialized when fence set

    /** Create new spliterator covering the given  range */
    ArrayListSpliterator(ArrayList list, int origin, int fence,
                         int expectedModCount) {
        this.list = list; // OK if null unless traversed
        this.index = origin;
        this.fence = fence;
        this.expectedModCount = expectedModCount;
    }

    private int getFence() { // initialize fence to size on first use
        int hi; // (a specialized variant appears in method forEach)
        ArrayList lst;
        if ((hi = fence) < 0) {
            if ((lst = list) == null)
                hi = fence = 0;
            else {
                expectedModCount = lst.modCount;
                hi = fence = lst.size;
            }
        }
        return hi;
    }

    public ArrayListSpliterator trySplit() {
        int hi = getFence(), lo = index, mid = (lo + hi) >>> 1;
        return (lo >= mid) ? null : // divide range in half unless too small
            new ArrayListSpliterator(list, lo, index = mid,
                                        expectedModCount);
    }

    public boolean tryAdvance(Consumer action) {
        if (action == null)
            throw new NullPointerException();
        int hi = getFence(), i = index;
        if (i < hi) {
            index = i + 1;
            @SuppressWarnings("unchecked") E e = (E)list.elementData[i];
            action.accept(e);
            if (list.modCount != expectedModCount)
                throw new ConcurrentModificationException();
            return true;
        }
        return false;
    }

    public void forEachRemaining(Consumer action) {
        int i, hi, mc; // hoist accesses and checks from loop
        ArrayList lst; Object[] a;
        if (action == null)
            throw new NullPointerException();
        if ((lst = list) != null && (a = lst.elementData) != null) {
            if ((hi = fence) < 0) {
                mc = lst.modCount;
                hi = lst.size;
            }
            else
                mc = expectedModCount;
            if ((i = index) >= 0 && (index = hi) <= a.length) {
                for (; i < hi; ++i) {
                    @SuppressWarnings("unchecked") E e = (E) a[i];
                    action.accept(e);
                }
                if (lst.modCount == mc)
                    return;
            }
        }
        throw new ConcurrentModificationException();
    }

    public long estimateSize() {
        return (long) (getFence() - index);
    }

    public int characteristics() {
        return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
    }
}
33、removeIf:删除所有满足特定条件的数组元素

这个方法会预先设置一个容器,final BitSet removeSet = new BitSet(size),其容器内部长度是整个数组的长度,默认是最差情况,即移除全部数组数据。

在循环中执行lambda表达式的判断结果,将符合的数据所在的数组下标位置,放入removeSet容器之中,同时进行移除数量计数

如果移除数量大于0,就执行移除,这里的移除类似于之前的remove *** 作,只不过这次的 *** 作更加的零散,首先计算出新数组的长度,然后将removeSet.nextClearBit(i)获取到需要的数据的索引,以此为依据获取到数组数据,做置换,然后清除掉所有newSize之后的数组数据。

public boolean removeIf(Predicate filter) {
    Objects.requireNonNull(filter);
    // figure out which elements are to be removed
    // any exception thrown from the filter predicate at this stage
    // will leave the collection unmodified
    int removeCount = 0;
    final BitSet removeSet = new BitSet(size);
    final int expectedModCount = modCount;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        @SuppressWarnings("unchecked")
        final E element = (E) elementData[i];
        if (filter.test(element)) {
            removeSet.set(i);
            removeCount++;
        }
    }
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }

    // shift surviving elements left over the spaces left by removed elements
    final boolean anyToRemove = removeCount > 0;
    if (anyToRemove) {
        final int newSize = size - removeCount;
        for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
            i = removeSet.nextClearBit(i);
            elementData[j] = elementData[i];
        }
        for (int k=newSize; k < size; k++) {
            elementData[k] = null;  // Let gc do its work
        }
        this.size = newSize;
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

    return anyToRemove;
}
 34、replaceAll:全部替换

这是一个使用lambda表达式的函数,其会循环执行将lambda表达式的结果替换到内部数组的 *** 作,同时依旧会执行边界检测和快速失败检测

@Override
@SuppressWarnings("unchecked")
public void replaceAll(UnaryOperator operator) {
    Objects.requireNonNull(operator);
    final int expectedModCount = modCount;
    final int size = this.size;
    for (int i=0; modCount == expectedModCount && i < size; i++) {
        elementData[i] = operator.apply((E) elementData[i]);
    }
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    modCount++;
}
 35、sort:排序方法

sort熟知的排序方法,可以看到,其参数必须继承Comparator

实际上,这个方法执行的是Arrays工具包内的比较方法。

@Override
@SuppressWarnings("unchecked")
public void sort(Comparator c) {
    final int expectedModCount = modCount;
    Arrays.sort((E[]) elementData, 0, size, c);
    if (modCount != expectedModCount) {
        throw new ConcurrentModificationException();
    }
    modCount++;
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/877177.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)