java集合进阶ArrayList源码
Posted 烟锁迷城
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java集合进阶ArrayList源码相关的知识,希望对你有一定的参考价值。
本篇文章多借鉴于b站账号:@河北王校长的直播,本人能力有限,阅读源码水平不足,此作为笔记以记录知识。
此文章分析的是ArrayList源码,最为常见的集合之一,希望能对读者有所帮助
目录
5.1、Arrays.aslist:转化为固定长度的ArrayList
5.2、Collections.emptyList():空EmptyList
10、lastIndexOf:判断数据的数组位置(倒序查找)
22、removeRange:范围移除(仅限于同包,子包或继承类)
31、forEach:迭代方法,forEach循环,可以写lambda表达式
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源码的主要内容,如果未能解决你的问题,请参考以下文章