JAVA-ArrayList 源码学习-201805
(基于jdk 9源码)
一.ArrayList简介
public class ArrayList<E> extends AbstractList<E> implements List<E>,RandomAccess,Cloneable,java.io.Serializable
List 接口的大小可变数组的实现类。实现了所有List的方法,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于 Vector 类,除了此类是不同步的。)
二.源码学习
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
1.DEFAULT_CAPACITY默认初始容量为10.
/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
2.不同的空数组对应不同的构造函数
transient Object[] elementData;
3.缓冲数组elementData:用于缓存数组,该数组长度即是ArrayList的容量capacity。
此处使用transient的原因是:ArrayList的序列化使用本类的private void writeObject/readObject (java.io.ObjectOutputStream s)方法。elementData数组中会有扩容后产生的空余容量,非最简。
writeObject方法
/**
* Saves the state of the {@code ArrayList} instance to a stream
* (that is, serializes it).
*
* @param s the stream
* @throws java.io.IOException if an I/O error occurs
* @serialData The length of the array backing the {@code ArrayList}
* instance is emitted (int), followed by all of its elements
* (each an {@code Object}) in the proper order.
*/
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject(); //将本类中非transient非static的字段写入ObjectOutputStream s
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size); //将size字段写入,这里再写一次,按照注释:保持与clone()兼容[与以前版本clone()相容?]
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]); //将size内的elementData[i]写入,空余容量不写入
}
if (modCount != expectedModCount) { //检查是否存在并发修改
throw new ConcurrentModificationException();
}
}
注:注意写入顺序,defaultWriteObject()一般写在第一个,原因是为了更好的兼容性。当一个类的新版本添加一个非transient字段时,若使用老版本class去反序列化新版本对象时,新加的字段将被忽略;若使用新版本class去反序列化老版本对象时,新加的字段将被赋默认值(Object:null,boolean:false,int:0...)。相应地,defaultReadObject()也一般写在第一位。
private int size;
4. size:ArrayList中元素的个数。
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);
}
}
5. 构造方法1:public ArrayList(int initialCapacity), initialCapacity自定义的初始容量 public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
6. 构造方法2:public ArrayList(), 初始容量为0public ArrayList(Collection<? extends E> c) {
elementData = c.toArray(); //将c转成数组并赋给elementData
if ((size = elementData.length) != 0) { //将elementData的length赋给size,在size!=0时,检验注释中提到的bug;size=0时,赋空数组
// defend against c.toArray (incorrectly) not returning Object[]
// (see e.g. https://bugs.openjdk.java.net/browse/JDK-6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class); //若出现toArray不返回Object[]的bug,则通过
} else { //Arrays.copyOf()重新复制给elementData
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
7. 构造方法3:public ArrayList(Collection<? extends E> c) 通过已有Collection c重构ArrayList。先把c通过toArray()转成数组并赋值给缓冲数组elementData,再把elementData的长度赋值给size。
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/
public boolean add(E e) {
modCount++; //结构性修改次数统计(包括add/remove...,不包括更改元素的值set),用于检测并发修改异常
add(e, elementData, size); //主要调用下面的私有方法实现功能
return true; //永远返回true
}
/**
* This helper method split out from add(E) to keep method
* bytecode size under 35 (the -XX:MaxInlineSize default value), //注释-要分开写一个调用方法的原因:貌似涉及jvm性能调优[勉强看看]
* which helps when add(E) is called in a C1-compiled loop. //把add(E)的字节码压缩到35个(MaxInlineSize :一行最多35个)
grow()方法
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if minCapacity is less than zero
*/
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity)); //还是Arrays.copyOf方法,这里传入size+1作为最小容量参数minCapacity
} //传入newCapacity()方法
private Object[] grow() {
return grow(size + 1);
}
newCapacity()方法
/**
* Returns a capacity at least as large as the given minimum capacity.
* Returns the current capacity increased by 50% if that suffices.
* Will not return a capacity greater than MAX_ARRAY_SIZE unless
* the given minimum capacity is greater than MAX_ARRAY_SIZE.
*
* @param minCapacity the desired minimum capacity
* @throws OutOfMemoryError if minCapacity is less than zero
*/
private int newCapacity(int minCapacity) {
// overflow-conscious code //考虑溢出
int oldCapacity = elementData.length; //旧容量
int newCapacity = oldCapacity + (oldCapacity >> 1); //new=old+old/2 [右移1]
if (newCapacity - minCapacity <= 0) { //当newCapacity<=minCapacity时,返回minCapacity
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) //若elementData为空,则返回默认容量10,与minCapacity间的最大值;
return Math.max(DEFAULT_CAPACITY, minCapacity);
if (minCapacity < 0) // overflow //若minCapacity<0,则抛出内存溢出Error:考虑前面代入的参数size+1,若
throw new OutOfMemoryError(); //size取int的最大值2^31-1,则此时minCapacity=size+1=-2^31;即若超限
return minCapacity; //符号位必然为1,此时为负数。
}
return (newCapacity - MAX_ARRAY_SIZE <= 0) //当newCapacity>minCapacity时,若newCapacity<=MAX_ARRAY_SIZE [2^31-9],
? newCapacity //返回newCapacity;否则调用hugeCapacity(minCapacity);
: hugeCapacity(minCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow //还是先验证是否溢出
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) //若minCapacity>2^31-9,则返回int的最大值2^31-1;否则,返回2^31-9.
? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
补:MAX_ARRAY_SIZE/**
* The maximum size of array to allocate (unless necessary). //jvm留了8个byte给数组的 header words头信息,具体参看:
* Some VMs reserve some header words in an array. //https://www.ibm.com/developerworks/java/library/j-codetoheap/index.html
* Attempts to allocate larger arrays may result in
* OutOfMemoryError: Requested array size exceeds VM limit
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
即一般地,若ArrayList中没有元素且未指定初始容量,add第一个元素时,将扩容至10;之后,若元素个数=容量,则扩容当前容量的50%,再add; 若元素个数<容量,则直接add。
9.再看另一个add(int index,E element)-指定位置添加
public void add(int index, E element) {
rangeCheckForAdd(index); //先检查index有无数组越界,若有,则抛出越界异常
modCount++; //结构性修改+1
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length) //若容量=元素个数,扩容
elementData = grow();
System.arraycopy(elementData, index,
elementData, index + 1, //使用System.arraycopy方法将index位置以后(包括index)的元素全部后移一位
s - index);
elementData[index] = element;
size = s + 1;
}
public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
用法:src数组位置从srcPos到srcPos+length-1的元素按序赋值给dest数组destPos到desPos+length-1位置
int[] a= {14,81,97,52,57,44};
int[] b= {0,1,0,1,0,1};
System.arraycopy(a, 2, b, 3, 2);
System.out.println("a"+Arrays.toString(a));
System.out.println("b"+Arrays.toString(b));
结果:a[14, 81, 97, 52, 57, 44] b[0, 1, 0, 97, 52, 1]/**
* Trims the capacity of this <tt>ArrayList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> instance.
*/
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}
10.public void trimToSize()用于去除空余容量,使容量=元素个数。
/**
* Trims the capacity of this <tt>ArrayList</tt> instance to be the
* list's current size. An application can use this operation to minimize
* the storage of an <tt>ArrayList</tt> instance.
*/
public void trimToSize() {
modCount++; //结构性修改+1
if (size < elementData.length) {
elementData = (size == 0) //当size<capacity时,若elementData为空,则赋空数组;
? EMPTY_ELEMENTDATA //若不为空,则使用Arrays.copyOf将容量控制为size
: Arrays.copyOf(elementData, size);
}
}
11.public int size()-返回size属性值12.public boolean isEmpty()- return size==0-判断list内元素个数是否为空
13.public int indexOf(Object o)-判断Object o在list中第一次出现的位置
public int lastIndexOf(Object o)-判断Object o在list中最后出现的位置[从后向前遍历]
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*/
public int indexOf(Object o) {
if (o == null) {
for (int i = 0; i < size; i++) //从0开始循环遍历得到o第一次出现的位置,分o==null和o!=null两种情况
if (elementData[i]==null)
return i;
} else {
for (int i = 0; i < size; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
14.public boolean contains(Object o)--判断list中是否包含o
/**
* Returns <tt>true</tt> if this list contains the specified element.
* More formally, returns <tt>true</tt> if and only if this list contains
* at least one element <tt>e</tt> such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this list is to be tested
* @return <tt>true</tt> if this list contains the specified element
*/
public boolean contains(Object o) {
return indexOf(o) >= 0; //这里直接调用indexOf(o),若o在list内,则indexOf(o)>=0,返回true;反之,返回false;
}
15.public E get(int index)-取list中指定index的元素
public E get(int index) {
rangeCheck(index); // 越界检查
return elementData(index); //直接返回elementData数组中指定index的元素
}
16.public E set(int index,E element)--改变指定index元素的值,返回原值public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element; //用elementData数组直接改,返回原值
return oldValue;
}
17.toArray方法--把list重新转成数组
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
if (a.length < size)
// Make a new array of a's runtime type, but my contents: //当size>数组a的长度,使用Arrays.copyOf复制list中所有元素
return (T[]) Arrays.copyOf(elementData, size, a.getClass()); //到以a.class创建长度为size的新数组,返回该数组。
System.arraycopy(elementData, 0, a, 0, size); //若size<=a.length,则把elementData中所有元素赋值给a数组0到
if (a.length > size) //size-1位置,并把a[size]设为null
a[size] = null; //根据注释,设为null,可以便于统计list的长度......
return a;
}
public Object[] toArray() {
return Arrays.copyOf(elementData, size); //还是用Arrays.copyOf返回数组
}
18.remove方法-删除第一次出现的该元素
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If the list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return <tt>true</tt> if this list contained the specified element
*/
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++) //从0开始遍历list,分null和非null两种情况,具体实现调用fastRemove
if (elementData[index] == null) { //若list中有该元素,可以删除,则返回true;否则返回false
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
fastRemove()方法
private void fastRemove(int index) {
modCount++; //结构性修改+1
int numMoved = size - index - 1; //需要移动的元素个数:index之后的元素全部向前移1位
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work //把elementData[size-1]设为null
}
public E remove(int index)-按index删除,实现和fastRemove差不多。 19.clear方法-清除list内全部元素
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++) //遍历,把所有元素都设为null,size=0
elementData[i] = null;
size = 0;
}
20.ensureCapacity(int minCapacity)--手动设置容量
/**
* Increases the capacity of this {@code ArrayList} instance, if
* necessary, to ensure that it can hold at least the number of elements
* specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
public void ensureCapacity(int minCapacity) {
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA //只有在minCapacity>现有容量,且若原数组为空,minCapacity>10时
&& minCapacity <= DEFAULT_CAPACITY)) { //才会执行扩容方法grow(minCapacity)
modCount++; //结构性修改+1
grow(minCapacity);
}
}
扩容后大小:一般地,取原容量的1.5倍与自设容量minCapacity之间的大的一个。因此,若要自定容量,最好大于1.5*原容量
下面测试一下在大容量情况下 预设容量与不预设之间的差异
int n=10000000;
ArrayList<Integer> list1=new ArrayList<>();
ArrayList<Integer> list2=new ArrayList<>();
long t1=System.currentTimeMillis();
list1.ensureCapacity(n);
for(int i=0;i<n;i++) {
list1.add(i);
}
long t2=System.currentTimeMillis();
for(int i=0;i<n;i++) {
list2.add(i);
}
long t3=System.currentTimeMillis();
System.out.println("预设. "+(t2-t1)+"ms");
System.out.println("不设. "+(t3-t2)+"ms");
结果:预设. 293ms 不设. 454ms
21.addAll方法-将一个collection添加入原list指定位置
/**
* Inserts all of the elements in the specified collection into this
* list, starting at the specified position. Shifts the element
* currently at that position (if any) and any subsequent elements to
* the right (increases their indices). The new elements will appear
* in the list in the order that they are returned by the
* specified collection's iterator.
*
* @param index index at which to insert the first element from the
* specified collection
* @param c collection containing elements to be added to this list
* @return {@code true} if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws NullPointerException if the specified collection is null
*/
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index); //越界检查
Object[] a = c.toArray(); //待添加collection c转成数组
modCount++; //结构性修改+1
int numNew = a.length;
if (numNew == 0) //若c为空,返回false
return false;
Object[] elementData;
final int s;
if (numNew > (elementData = this.elementData).length - (s = size)) //若空余容量<待添加数组的元素个数,则扩容
elementData = grow(s + numNew);
int numMoved = s - index; //原数组中需要移动的元素个数:从index到s
if (numMoved > 0)
System.arraycopy(elementData, index, //将原数组index到index+numnew-1腾空,原index到size的元素全部向后移
elementData, index + numNew,
numMoved);
System.arraycopy(a, 0, elementData, index, numNew); //再把a数组的元素全部填充到原index到index+numnew-1的位置
size = s + numNew; //返回true
return true;
}
public boolean addAll(Collection<? extends E> c)方法和以上方法思路基本一致,只是全在队尾加入。22.removeRange(int from,int to)--删除从from到to的元素
/**
* Removes from this list all of the elements whose index is between
* {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.
* Shifts any succeeding elements to the left (reduces their index).
* This call shortens the list by {@code (toIndex - fromIndex)} elements.
* (If {@code toIndex==fromIndex}, this operation has no effect.) //注释:若from==to,则不产生变化
*
* @throws IndexOutOfBoundsException if {@code fromIndex} or
* {@code toIndex} is out of range
* ({@code fromIndex < 0 ||
* toIndex > size() ||
* toIndex < fromIndex})
*/
protected void removeRange(int fromIndex, int toIndex) {
if (fromIndex > toIndex) {
throw new IndexOutOfBoundsException( //检查两个坐标是否正确,不正确则抛出异常
outOfBoundsMsg(fromIndex, toIndex));
}
modCount++; // 结构性修改+1
shiftTailOverGap(elementData, fromIndex, toIndex); //调用下面的函数shiftTailOverGap()实现功能
}
/** Erases the gap from lo to hi, by sliding down following elements. */
private void shiftTailOverGap(Object[] es, int lo, int hi) {
System.arraycopy(es, hi, es, lo, size - hi); //把es中从hi到size-1的元素,按序赋值到从lo到lo+size-hi-1的元素
for (int to = size, i = (size -= hi - lo); i < to; i++) //再把从lo+size-hi到size-1的元素赋null
es[i] = null;
}
23.removeAll方法-删除list中包含的c内元素public boolean removeAll(Collection<?> c) {
return batchRemove(c, false, 0, size); //直接调下面的batchRemove(c,false ,0,size)
}
boolean batchRemove(Collection<?> c, boolean complement,
final int from, final int end) {
Objects.requireNonNull(c); //检查c是否为空,若为空,抛出空指针异常
final Object[] es = elementData;
final boolean modified;
int r;
// Optimize for initial run of survivors //优化选出第一个不符合条件的值
for (r = from; r < end && c.contains(es[r]) == complement; r++) //若complement=true,表示选出第一个c中不包含元素的下标;
; //complement=false,选出第一个list与c有交集的元素下标
if (modified = (r < end)) {//这里若r=end,则未选到初始下标,未对list作修改,返回false;若r<end,表示选到,进入if内返回true
int w = r++; //第一个不符合条件的下标,可设为r0;此时令w=r0,r=r0+1;
try {
for (Object e; r < end; r++) //此时r从r0+1到end-1遍历,若存在符合条件的数组元素es[r],则把按序赋给es[w],
if (c.contains(e = es[r]) == complement) //(w从r0开始自增),即es[r0]到es[w-1]为选出符合条件的元素覆盖第一个不符元素
es[w++] = e; //条件:若complement=true,选出c中存在的元素;false,选出c中不存在元素
} catch (Throwable ex) {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws. //这里防止contains()抛出异常,并要与AbstractCollection中的removeAll/retainAll保持一致
System.arraycopy(es, r, es, w, end - r); //对抛出异常后的es[r]不做检验,直接放到w位以后
w += end - r; //结构性修改次数+r-w次 因只删了r-w个就抛出异常了
throw ex;
} finally {
modCount += end - w; //结构型修改次数+end-w 次 最后删了end-w个元素
shiftTailOverGap(es, w, end); //System.arraycopy(es,end,es,w,size-end)把end到size的元素移到w之后,后面赋null
}
}
return modified;
}
batchRemove方法实现过程:先选出不符合条件的第一个元素es[r0],在从r0向后遍历,取符合条件的每个元素,按序赋给从es[r0]开始的原数组元素。
其中removeAll(Collection c)方法调用batchRemove(Collection c, false,0,size),即先选出list中第一个c中存在的元素es[r0],再从r0向后遍历,取c中不存在元素,按序赋给从es[r0]开始的原数组元素。
24.retainAll方法-只保留list中包含的c内元素
public boolean retainAll(Collection<?> c) {
return batchRemove(c, true, 0, size); //也是调用batchRemove,实现与removeAll相似
}
参考:
https://blog.csdn.net/u011392897/article/details/57105709
https://blog.csdn.net/u012877472/article/details/50852933
https://stackoverflow.com/questions/34250207/in-java-8-why-is-the-default-capacity-of-arraylist-now-zero
https://stackoverflow.com/questions/17235877/need-of-defaultreadobject-and-defaultwriteobject
https://blog.csdn.net/u010723709/article/details/45647447
https://blog.csdn.net/tingfeng96/article/details/52261219
https://stackoverflow.com/questions/35756277/why-the-maximum-array-size-of-arraylist-is-integer-max-value-8#
https://www.ibm.com/developerworks/java/library/j-codetoheap/index.html
https://www.cnblogs.com/vinozly/p/5171227.html