本篇文章多借鉴于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 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:转化为固定长度的ArrayListArrays.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 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 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 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 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 super E> filter) {
Objects.requireNonNull(filter);
return false;
}
@Override
public void replaceAll(UnaryOperator 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 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 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 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 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 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 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 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 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 = 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 super E> 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 super E> 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 super E> 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 super E> 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 super E> c) {
final int expectedModCount = modCount;
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)