ArrayList源码分析
继承实现
public class ArrayList<E> extends AbstractList<E> implements Cloneable, Serializable, RandomAccess
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
public interface List<E> extends Collection<E>
public abstract class AbstractCollection<E> implements Collection<E>
public interface Collection<E> extends Iterable<E>
Iterable
接口只有一个方法iterator()
,返回一个迭代器。Java
的集合主要按两种接口分类:Collection
,Map
。Collection
接口实现Iterable
接口,作为集合的一个根接口,定义了一组对象和它的子类需要实现的方法:add()
、addAll()
、clear()
、contains()
、containsAll()
、equals()
、hashCode()
、isEmpty()
、iterator()
、remove()
、removeAll()
、retainAll()
、size()
、toArray()
AbstractCollection
抽象类实现Collection
接口,并对一些方法进行实现。有iterator()
和size()
两个抽象方法,其子类必须实现。AbstractCollection
是Java
集合框架中Collection
接口 的一个直接实现类,Collection
下一系的大多数子类都继承AbstractCollection
,比如List
的实现类,Set
的实现类。List
接口继承Collection
接口,定义了一系列更加适合线性集合的方法。是一个元素有序的、可以重复、可以为 null 的集合。最常使用的几种List
实现类是ArrayList
,LinkedList
和Vector
。AbstractList
继承自AbstractCollection
抽象类,实现了List
接口 ,是ArrayList
和AbstractSequentiaList
的父类。
重要属性
说实话,ArrayList
的属性没啥!
// 容量增量。如果数组容量满了,增加如下大小
private static final int MIN_CAPACITY_INCREMENT = 12;
// 数组的大小
int size;
// 真实保存数据的数组
transient Object[] array;
transient
:不会参与序列化。
构造方法
ArrayList
有3个构造方法:
1.无参构造
public ArrayList() {
array = EmptyArray.OBJECT;
}
public static final Object[] OBJECT = new Object[0];
无参构造是直接实例化了一个静态final
数组。size=0
.
2.容量构造
public ArrayList(int capacity) {
if (capacity < 0) {
throw new IllegalArgumentException("capacity < 0: " + capacity);
}
array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
}
先检查容量。
如果容量为0,那么跟无参构造一样。
如果>0,就new Object[capacity]
。
3.集合构造
public ArrayList(Collection<? extends E> collection) {
if (collection == null) {
throw new NullPointerException("collection == null");
}
Object[] a = collection.toArray();
if (a.getClass() != Object[].class) {
Object[] newArray = new Object[a.length];
System.arraycopy(a, 0, newArray, 0, a.length);
a = newArray;
}
array = a;
size = a.length;
}
这个构造是通过将新集合的数据拷贝到该集合中。
首先,Collection
是一个接口,所有的集合:包括List
, Set
, Map
, Vector
等都实现了这个接口。接口内容有:增、删、改、查、迭代器、是否包括、是否为空等等。
toArray()
转成Object[]
,然后判断a.getClass()
。
如果集合是new ArrayList()
生成的,那么Class
就是ArrayList
。
拷贝方式:先new
一个同等大小的数组,然后System.arraycopy()
数据,最后赋值给a
。
刷新array
和size
。
功能函数
add
add
方法有两个
@Override public boolean add(E object) {
Object[] a = array;
int s = size;
if (s == a.length) {
Object[] newArray = new Object[s +
(s < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : s >> 1)];
System.arraycopy(a, 0, newArray, 0, s);
array = a = newArray;
}
a[s] = object;
size = s + 1;
modCount++;
return true;
}
用临时变量是一个好习惯!
判断了数组的大小和集合的大小是否一致,如果一致代表数组已满。
满了就要重新分配空间,newArray
就是重新分配空间的数组。
分配规则:如果容量增量的一半大于当前数组长度,就增量小点。增量MIN_CAPACITY_INCREMENT
。如果不大于,就增量MIN_CAPACITY_INCREMENT
的2倍。然后得到的大小再加上当前数组的长度,就是newArray
的长度。
随后,拷贝数据到newArray
中。给size
号位置添加元素。
刷新数组和size
。
至于modCount
不用管,这个东西是你无论增删查改都会增加的一个计量数。
第2个add
方法
这个方法比第1个add
方法略复杂,因为它是指定位置的插入元素。
@Override public void add(int index, E object) {
Object[] a = array;
int s = size;
if (index > s || index < 0) {
throwIndexOutOfBoundsException(index, s);
}
if (s < a.length) {
System.arraycopy(a, index, a, index + 1, s - index);
} else {
// assert s == a.length;
Object[] newArray = new Object[newCapacity(s)];
System.arraycopy(a, 0, newArray, 0, index);
System.arraycopy(a, index, newArray, index + 1, s - index);
array = a = newArray;
}
a[index] = object;
size = s + 1;
modCount++;
}
先是赋值和判断。
接着判断,如果集合大小小于数组大小。那么就说明数组有空间插入,直接System.arraycopy()
拷贝元素,从index
开始拷贝。以便留坑给object
。
如果不小于,说明数组已满。得扩容。newCapacity()
就是一个扩容方法
private static int newCapacity(int currentCapacity) {
int increment = (currentCapacity < (MIN_CAPACITY_INCREMENT / 2) ?
MIN_CAPACITY_INCREMENT : currentCapacity >> 1);
return currentCapacity + increment;
}
可以看到,扩容规则和第1个add
方法的扩容规则完全一样!只不过这里单独提取出来了而已。
newArray
新数组,然后System.arraycopy()
分段拷贝2次。第1次从0拷贝到index
,第2次从index
拷贝到最后。留坑给object
。
刷新数组和size。
addAll
两个addAll
方法
第1个addAll()
@Override public boolean addAll(Collection<? extends E> collection) {
Object[] newPart = collection.toArray();
int newPartSize = newPart.length;
if (newPartSize == 0) {
return false;
}
Object[] a = array;
int s = size;
int newSize = s + newPartSize; // If add overflows, arraycopy will fail
if (newSize > a.length) {
int newCapacity = newCapacity(newSize - 1); // ~33% growth room
Object[] newArray = new Object[newCapacity];
System.arraycopy(a, 0, newArray, 0, s);
array = a = newArray;
}
System.arraycopy(newPart, 0, a, s, newPartSize);
size = newSize;
modCount++;
return true;
}
同样的赋值和判断。
得到当前集合和新数组的总长度newSize
。
如果newSize
大于当前数组的长度,就得扩容。而扩容方式,知道了add
就知道了。
这个是newArray
新数组,先拷贝当前集合的数据。之后,再拷贝新数组的数据。
刷新数组和size
。
第2个addAll()
方法
@Override
public boolean addAll(int index, Collection<? extends E> collection) {
int s = size;
if (index > s || index < 0) {
throwIndexOutOfBoundsException(index, s);
}
Object[] newPart = collection.toArray();
int newPartSize = newPart.length;
if (newPartSize == 0) {
return false;
}
Object[] a = array;
int newSize = s + newPartSize; // If add overflows, arraycopy will fail
if (newSize <= a.length) {
System.arraycopy(a, index, a, index + newPartSize, s - index);
} else {
int newCapacity = newCapacity(newSize - 1); // ~33% growth room
Object[] newArray = new Object[newCapacity];
System.arraycopy(a, 0, newArray, 0, index);
System.arraycopy(a, index, newArray, index + newPartSize, s-index);
array = a = newArray;
}
System.arraycopy(newPart, 0, a, index, newPartSize);
size = newSize;
modCount++;
return true;
}
这个也很好理解了!
如果newSize
小于等于当前数组长度,直接分段拷贝数据。
如果大于,就newArray
,再分段拷贝数据。
clear
@Override public void clear() {
if (size != 0) {
Arrays.fill(array, 0, size, null);
size = 0;
modCount++;
}
}
如果有数据,先将所有元素置为0.
再将size
置为0.
clone
@Override public Object clone() {
try {
ArrayList<?> result = (ArrayList<?>) super.clone();
result.array = array.clone();
return result;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
先clone
新的ArrayList
。
然后将当前集合的array
数组赋值给新集合的array
数组。
最后返回新集合。
ensureCapacity
public void ensureCapacity(int minimumCapacity) {
Object[] a = array;
if (a.length < minimumCapacity) {
Object[] newArray = new Object[minimumCapacity];
System.arraycopy(a, 0, newArray, 0, size);
array = newArray;
modCount++;
}
}
这个方法是确保容量的方法。
如果当前真实数组的长度小于你指定的长度,那么就以你指定的长度重新创建数组,拷贝数据。最后刷新数组。
get size isEmpty
@SuppressWarnings("unchecked") @Override public E get(int index) {
if (index >= size) {
throwIndexOutOfBoundsException(index, size);
}
return (E) array[index];
}
@Override public int size() {
return size;
}
@Override public boolean isEmpty() {
return size == 0;
}
get
没啥好说的,直接返回数组元素!
另外两个也一样。
contains indexOf lastIndexOf
@Override public boolean contains(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
return true;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
return true;
}
}
}
return false;
}
@Override public int indexOf(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
return i;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
return i;
}
}
}
return -1;
}
@Override public int lastIndexOf(Object object) {
Object[] a = array;
if (object != null) {
for (int i = size - 1; i >= 0; i--) {
if (object.equals(a[i])) {
return i;
}
}
} else {
for (int i = size - 1; i >= 0; i--) {
if (a[i] == null) {
return i;
}
}
}
return -1;
}
这3个放一起讲了,因为这3个很类似。
都是直接for
循环,从0开始到最后或者从最后开始到0遍历。挨个比较!
有趣的是,null
也是一种元素。也参与了比较。
remove
@Override public E remove(int index) {
Object[] a = array;
int s = size;
if (index >= s) {
throwIndexOutOfBoundsException(index, s);
}
@SuppressWarnings("unchecked") E result = (E) a[index];
System.arraycopy(a, index + 1, a, index, --s - index);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return result;
}
@Override public boolean remove(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
}
return false;
}
第1个remove
方法,通过位置删除:
判断范围。
将index
位置的元素给result
。然后将index+1
开始到最后的这段元素整体前移1位。
将size
位置为null
。刷新size
并返回result
。
第2个remove
方法,通过数据删除:
和contains
一样的寻找方式,和第1个remove
一样的删除方式!
removeRange
@Override public E remove(int index) {
Object[] a = array;
int s = size;
if (index >= s) {
throwIndexOutOfBoundsException(index, s);
}
@SuppressWarnings("unchecked") E result = (E) a[index];
System.arraycopy(a, index + 1, a, index, --s - index);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return result;
}
@Override public boolean remove(Object object) {
Object[] a = array;
int s = size;
if (object != null) {
for (int i = 0; i < s; i++) {
if (object.equals(a[i])) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
} else {
for (int i = 0; i < s; i++) {
if (a[i] == null) {
System.arraycopy(a, i + 1, a, i, --s - i);
a[s] = null; // Prevent memory leak
size = s;
modCount++;
return true;
}
}
}
return false;
}
这是删除一段元素的方法。
从fromIndex
到toIndex
这一段的元素删除。
方式:将toIndex
开始往后的数据拷贝到fromIndex
位置开始往后。
剩余rangeSize
个位置,应该置为0.
fill填充0。刷新size。
set
@Override public E set(int index, E object) {
Object[] a = array;
if (index >= size) {
throwIndexOutOfBoundsException(index, size);
}
@SuppressWarnings("unchecked") E result = (E) a[index];
a[index] = object;
return result;
}
easy了!
直接给数组元素赋值。
toArray
@Override public Object[] toArray() {
int s = size;
Object[] result = new Object[s];
System.arraycopy(array, 0, result, 0, s);
return result;
}
拷贝数据到新创建的数组中并返回!
Other
剩下的主要就是迭代器了。hasNext
、next
之类的。还有readObject
、writeObject
等一些不重要的方法了。