ArrayList源码分析
ArrayList 底层数据结构
- 是List一个可调整大小的数组
数组结构的优缺点:
- 数组查询快,根据地址和索引直接获取元素
- 数组增删慢,每次都需要创建一个新的数组,且移动元素的位置
ArrayList继承关系
Serializable 标记性接口
-
类的序列化由实现java.io.Serializable接口的类启用。不实现此接口的类将不会使任何状态序列化或反序列化。可序列化的所有子类型都是可序列化的。标记性接口没有方法或属性。
序列化:将对象的数据写入文件
反序列化:将文件中的对象数据读取出来
Cloneable 标记性接口
-
一个类实现
Cloneable
接口来指示Object.clone()
方法,该方法对于该类的实例进行属性的复制是合法的。在不实现Cloneable 接口的实例上调用对象的克隆方法会抛出异常CloneNotSupportedException
。 -
clone条件:
- 实现 Cloneable 接口
- 重写clone 方法
public Object clone() { try { ArrayList<?> v = (ArrayList<?>) super.clone(); // Object.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); } }
浅克隆:基本数据类型可以完全复制,引用类型不可以,仅仅是拷贝了一份引用
深克隆:修改引用类型数据也不互相影响
RandomAccess 标记性接口
-
表明实现了这个接口的集合是支持快速随机访问的。
-
使用
for
循环的方式获取数据的性能优于用iterator
迭代器方式开发过程中,先判断返回的list 是否有实现RandomAccess 接口,(list instanceof RandomAccess),再决定用那种遍历方式。
ArrayList源码分析
构造方法
无参构造
// 第一次调用add(E e)方法时才创建一个容量为10的数组
public ArrayList() {
// 无参构造方法,创建一个初始容量为0的空数组
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
// Object[] elementData
}
带初始容量的构造
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);
}
}
带单列集合的构造
public ArrayList(Collection<? extends E> c) {
// 构造方法中的集合转数组
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// 防止c.toArray() 不返回Object[]
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// 空数组
this.elementData = EMPTY_ELEMENTDATA;
}
}
add源码分析
list末尾添加一个元素:
public boolean add(E e) {
// 并发修改的次数+1
modCount++;
// size当前元素的个数
add(e, elementData, size);
return true;
}
private void add(E e, Object[] elementData, int s) {
// 当前元素的个数是否等于原数组的长度
if (s == elementData.length)
// 进入扩容
elementData = grow();
// 扩容后进行元素的赋值
elementData[s] = e;
// 元素个数+1
size = s + 1;
}
private Object[] grow() {
// 元素个数+1作为最小容量
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
// 调用newCapacity算出扩容后的容量,再调用Arrays.copyOf()把原数组的元素复制到扩容后的数组
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private int newCapacity(int minCapacity) {
// 有溢出意识的代码
int oldCapacity = elementData.length;
// 扩容为原容量的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity <= 0) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) //DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组
return Math.max(DEFAULT_CAPACITY, minCapacity); //DEFAULT_CAPACITY = 10
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
在list指定位置添加一个元素:
public void add(int index, E element) {
// 判断下标是否合法
rangeCheckForAdd(index);
// 并发修改次数+1
modCount++;
final int s;
Object[] elementData;
// 判断是否需要扩容,当元素个数等于数组长度扩容
if ((s = size) == (elementData = this.elementData).length)
elementData = grow();
// 将要插入的下标后所有的元素向后移动一个位置
System.arraycopy(elementData, index,
elementData, index + 1,
s - index);
// 将元素赋值给数组指定位置
elementData[index] = element;
// 元素个数+1
size = s + 1;
}
private void rangeCheckForAdd(int index) {
// 判断下标是否合法,下标大于元素的个数或下标小于0时不合法抛IndexOutOfBoundsException异常
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
添加一个单列集合:
public boolean addAll(Collection<? extends E> c) {
// 将单列集合转为数组,底层调用Arrays.copyOf()方法
Object[] a = c.toArray();
// 并发修改的次数 +1
modCount++;
// 算出要添加的数组长度
int numNew = a.length;
// =0, 返回false, 表示添加失败
if (numNew == 0)
return false;
Object[] elementData;
final int s;
// numNew > 0 - 0
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
// 调用System.arraycopy将要添加的单列集合元素添加到新的list
System.arraycopy(a, 0, elementData, s, numNew);
// 修改元素个数
size = s + numNew;
return true;
}
在list指定位置添加一个单列集合:
public boolean addAll(int index, Collection<? extends E> c) {
// 判断要添加位置的下标是否合法
rangeCheckForAdd(index);
// 将集合转为Object数组
Object[] a = c.toArray();
// 并发修改次数 +1
modCount++;
// 算出Object数组的长度
int numNew = a.length;
if (numNew == 0)
return false;
Object[] elementData;
final int s;
// 判断数组长度是否大于集合中剩余的元素个数来进行扩容
if (numNew > (elementData = this.elementData).length - (s = size))
elementData = grow(s + numNew);
// 根据集合中元素的个数-要存的索引位置算出集合中要移动的个数
int numMoved = s - index;
// 如果大于0, 调用System.arraycopy方法进行数组元素的复制,index + numNew算出新下标
if (numMoved > 0)
System.arraycopy(elementData, index,
elementData, index + numNew,
numMoved);
// 才真正将数据源中的元素添加到集合中
System.arraycopy(a, 0, elementData, index, numNew);
// 重新计算集合元素的个数
size = s + numNew;
return true;
}
set源码分析
public E set(int index, E element) {
// 判断index是否合法, 当index下标小于0或大于等于集合中元素的个数时抛异常
Objects.checkIndex(index, size);
// 获得index下标的旧值
E oldValue = elementData(index);
// 修改下标的值
elementData[index] = element;
// 返回旧值
return oldValue;
}
public static int checkIndex(int index, int length) {
return Preconditions.checkIndex(index, length, null);
}
int checkIndex(int index, int length,
BiFunction<String, List<Integer>, X> oobef) {
if (index < 0 || index >= length)
throw outOfBoundsCheckIndex(oobef, index, length);
return index;
}
get源码解析
public E get(int index) {
// 判断index是否合法, 当index下标小于0或大于等于集合中元素的个数时抛异常
Objects.checkIndex(index, size);
// 返回index位置的元素
return elementData(index);
}
toString() 源码解析
public abstract class AbstractCollection<E> implements Collection<E> {
public String toString() {
// 获取迭代器
Iterator<E> it = iterator();
// 如果迭代器没有元素,直接返回空
if (! it.hasNext())
return "[]";
// 创建一个StringBuilder对象
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
// 调用迭代器的方法取出元素
E e = it.next();
sb.append(e == this ? "(this Collection)" : e);
// 如果没有元素了, 调用toString()将StringBulilder对象转成字符串
if (! it.hasNext())
return sb.append(']').toString();
// 如果有元素就调用StringBuilder的append方法追加元素
sb.append(',').append(' ');
}
}
}
迭代器iterator源码分析
public Iterator<E> iterator() {
return new Itr();
}
// ArrayList的迭代器源码, 不同集合实现迭代器源码的方式不同
private class Itr implements Iterator<E> {
int cursor; // 光标, 默认值是0
int lastRet = -1; // 记录-1
// 将集合的实际修改次数赋值给预期修改次数
int expectedModCount = modCount;
// prevent creating a synthetic constructor
Itr() {}
// 调用hasNext() 判断集合是否有元素, 比较光标cursor和集合中元素的个数size是否不相等
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
// 检查集合实际修改次数是否不等于预期修改次数, 不等于则抛出并发修改异常
checkForComodification();
// 将光标赋值给i
int i = cursor;
// 如果i >= 集合中的元素,说明没有元素了,抛出没有元素异常
if (i >= size)
throw new NoSuchElementException();
// 把集合存储数据的数组地址赋值该方法的局部变量
Object[] elementData = ArrayList.this.elementData;
// 如果 i >= 数组长度,抛出并发修改异常
if (i >= elementData.length)
throw new ConcurrentModificationException();
// 光标向下移动
cursor = i + 1;
// 从数组中取出元素并返回
return (E) elementData[lastRet = i];
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int size = ArrayList.this.size;
int i = cursor;
if (i < size) {
final Object[] es = elementData;
if (i >= es.length)
throw new ConcurrentModificationException();
for (; i < size && modCount == expectedModCount; i++)
action.accept(elementAt(es, i));
// update once at end to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
}
final void checkForComodification() {
// 判断集合实际修改次数是否不等于预期修改次数
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
并发修改异常
/**
测试代码=====
**/
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if (s.equals("3")) {
list.remove("3");
}
}
public boolean remove(Object o) {
final Object[] es = elementData;
final int size = this.size;
int i = 0;
// java的label语法, 通过命名一个语句块name, 然后调用 break name跳出该语句块
found: {
// 判断要删除的元素是否为null
if (o == null) {
for (; i < size; i++)
if (es[i] == null)
break found;
} else {
for (; i < size; i++)
if (o.equals(es[i]))
break found;
}
return false;
}
// 调用fastRemove进行删除
fastRemove(es, i);
return true;
}
private void fastRemove(Object[] es, int i) {
// 集合实际修改次数 +1
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
// (newSize - i) = 计算要被移动的元素个数
System.arraycopy(es, i + 1, es, i, newSize - i);
// 让删除的元素置为null, 为了尽快被垃圾回收
es[size = newSize] = null;
}
在调用add()方法时,实际修改次数会+1,而在获取iterator迭代器时只会执行一次将实际修改次数赋值给预期修改次数,在调用remove(Object o)删除元素时,实际修改次数也会+1,最终导致在下一次next() 的时候检查到实际修改次数不等于预期修改次数,从而抛出并发修改异常。
并发修改异常特殊情况
/**
测试代码=====
**/
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if (s.equals("2")) {
list.remove("2");
}
}
当要删除的元素在集合的倒数第二个位置的时候,不会产生并发修改异常。
原因是 :因为在调用hasNext方法时,光标cursor的值和元素个数的值size一样,返回false,不会继续调用next方法,也就不会去检查实际修改次数是否不等于预期修改次数。
iterator的默认方法remove
/**
测试代码=====
**/
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String s = iterator.next();
if (s.equals("3")) {
iterator.remove();
}
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
// 调用remove()方法,每次会对预期修改次数重新赋值,不会产生并发修改异常
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
clear源码分析
public void clear() {
// 并发修改次数 +1
modCount++;
final Object[] es = elementData;
for (int to = size, i = size = 0; i < to; i++)
// 把数组的每一个位置元素都置为null,让垃圾回收器尽快回收
es[i] = null;
}
contains源码分析
public boolean contains(Object o) {
// 调用 indexOf(o)方法,如果返回-1,说明没有找到,返回false; 如果返回值>=0,说明找到了,返回true
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
return indexOfRange(o, 0, size);
}
int indexOfRange(Object o, int start, int end) {
Object[] es = elementData;
// 如果contains的参数是null
if (o == null) {
for (int i = start; i < end; i++) {
if (es[i] == null) {
return i;
}
}
} else {
for (int i = start; i < end; i++) {
if (o.equals(es[i])) {
// 返回数组下标
return i;
}
}
}
return -1;
}
isEmpty源码分析
public boolean isEmpty() {
// 判断元素个数是否等于0
return size == 0;
}