ArrayList 源码分析
当前分析的源码版本为
jdk 1.7.0_80
ArrayList
是一个动态数组容器类。下方的代码为 ArrayList
的定义。
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// TODO ...
}
一、add()
方法的扩容原理是什么
先看源码
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
代码中的 elementData
和 size
是 ArrayList
类的两个成员变量。
transient Object[] elementData; // non-private to simplify nested class access
private int size;
代码注释中的 Increments modCount!!
在遍历的方法中再讲。modCount
代表容器被修改的次数。
首先调用 ensureCapacityInternal(size + 1)
方法确保容量是够的。然后对 elementData[size++]
进行赋值,最后 size
加 1。以下为 ensureCapacityInternal()
方法的代码。
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow()
方法的主要内容为
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);
}
扩容的核心的代码是 int newCapacity = oldCapacity + (oldCapacity >> 1);
,右移 >>
一位相当于除以 2。可以看出容器的容量扩大了原来的 1.5 倍。
二、remove()
方法
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;
}
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
remove()
方法的代码相对简单,主要是调用了 System.arraycopy()
方法对数组 elementData
自身进行复制,起始位置从 index+1
复制到 index
。然后将索引位置在 size--
的元素的引用置为 null
, 最后 size
减一。
三、ArraysList
中的向前遍历
ArrayList
类中维护了一个私有的内部类 ListItr
,ListItr
实现了 ListIterator
接口。以下为 ListItr
的定义。
private class ListItr extends Itr implements ListIterator<E> {
// TODO ...
}
接口 ListIterator
继承了 Iterator
接口,并新增了几个新的方法。下面是 ListIterator
的方法。
public interface ListIterator<E> extends Iterator<E> {
boolean hasNext();
E next();
boolean hasPrevious();
E previous();
int nextIndex();
int previousIndex();
void remove();
void set(E e);
void add(E e);
}
来看一个简单的降序遍历的例子。
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3));
ListIterator<Integer> descItr = list.listIterator(list.size());
while (descItr.hasPrevious()) {
Integer previous = descItr.previous();
System.out.println(previous);
}
}
输出
3
2
1
四、modCount
有什么作用
modCount
是 AbstractList
中的一个成员变量,记录容器类结构发生变化的次数,使容器结构发生变化的方法比如有 add()
和 remove()
方法。
protected transient int modCount = 0;
接下要从方法 iterator()
说起。
public Iterator<E> iterator() {
return new Itr();
}
Itr
是 ArrayList
中的一个内部类,实现了 Iterator
接口。
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;
public boolean hasNext(){...}
public E next() {...}
public void remove() {...}
final void checkForComodification() {...}
}
cursor
是下一个要返回的元素的索引值。lastRet
是最后一次要返回的元素的索引值。expectModCount
表示期望地修改次数。在初始化迭代器的时候复赋值为 modCount
。
下面代码中要通过迭代器删除 list
中的一个元素,并解释迭代器中每个方法的工作原理。
public static void main(String[] args) {
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
Iterator<Integer> iterator = list.iterator();
Integer next;
while(iterator.hasNext()) {
next = iterator.next();
if (next == 3) {
iterator.remove();
}
}
System.out.println(list.size());
}
while
循环中,先执行 hasNext()
方法,判断元素的索引位置是否超过容器容量。
public boolean hasNext() {
return cursor != size;
}
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];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
next()
方法先调用 checkForComodification()
检查容器的结构是否发生变化。在方法 checkForComodification()
中,如果 modCount != expectedModCount
就抛出异常 throw new ConcurrentModificationException();
。检查成功后对下一个元素的索引变量 cursor
进行赋值,然后赋值给变量 lastRet
,最后返回当前索引位置所在的元素。
下面是删除方法 remove()
的源代码。
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();
}
}
执行 remove()
方法的第一步就是对 lastRet
的检查。所以在使用迭代器遍历时,调用 remove()
方法前一定要调用 next()
方法。在 next()
方法的结尾处会对 lastRet
方法进行赋值,否则 lastRet
的初始值为 -1,就会抛出异常 java.lang.IllegalStateException
。
接下来调用 ArrayList
的 remove()
方法。然后对 expectModCount
进行赋值,保证容器结构的一致。
五、为什么在 foreach
方法中删除元素会抛异常
下面使用 foreach
语法删除一个元素。
public static void main(String[] args) {
List<Integer> list = new ArrayList<>(5);
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
for (Integer i : list) {
if (i == 5) {
list.remove(i);
}
}
}
运行后控制台抛出异常。
Exception in thread "main" java.util.ConcurrentModificationException
foreach
是 java 中的一颗语法糖,编译后的字节码文件是这样的。
public static void main(String[] args) {
List<Integer> list = new ArrayList();
list.add(1);
list.add(2);
list.add(3);
Iterator<Integer> iterator = list.iterator();
System.out.println(list.size());
List<Integer> list1 = new ArrayList();
list1.add(1);
list1.add(2);
list1.add(3);
Iterator i$ = list1.iterator();
while(i$.hasNext()) {
Integer integer = (Integer)i$.next();
if (integer == 3) {
list1.remove(integer);
}
}
}
上面代码中的 remove()
方法如下。
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;
}
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
}
程序最后走到了 fastRemove()
方法中。fastRemove()
方法和迭代器中的 remove()
方法不同,程序中并没有维护 expectedModCount = modCount
。所以在 next()
方法中执行 checkForComodification()
,发现 expectedModCount
和 modCount
不同,容器的结构发生了变化,就会抛出异常 throw new ConcurrentModificationException()
。
六、ArrayList
中的其他方法
在开发过程中还有一些 ArraysList
中的常用方法。
// 构造器方法
public ArrayList(int initialCapacity) {...}
public ArrayList() {...}
public ArrayList(Collection<? extends E> c) {...}
// 判断容器中元素的个数
public int size() {...}
// 判断元素是否为空
public boolean isEmpty() {...}
// 是否包含某个元素
public boolean contains(Object o) {...}
// 找出元素的索引位置
public int indexOf(Object o) {...}
public int lastIndexOf(Object o) {...}
// 将容易转换为数组
public Object[] toArray() {...}
public <T> T[] toArray(T[] a) {...}
// 获取元素
public E get(int index) {...}
// 替换元素
public E set(int index, E element) {...}
(完)
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于