ArrayList
ArrayList是一个被频繁使用的类, 本篇源码学习主要围绕着构造器/add/get/set/remove以及一些简单的api来学习, 首先得明白ArrayList的继承关系
在List中通过继承了Collection接口, 在iterable接口里定义了返回iterator的方法, 所以这就是List可以进行迭代遍历的原因
//编译前
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
for(Integer i: list) {
System.out.println(i);
}
list.remove(Integer.valueOf(1));
System.out.println(list.get(0));
}
// 编译后
public static void main(String[] args) {
List<Integer> list = new ArrayList();
list.add(1);
list.add(2);
Iterator var2 = list.iterator();
while(var2.hasNext()) {
Integer i = (Integer)var2.next();
System.out.println(i);
}
list.remove(1);
System.out.println(list.get(0));
}
可以看见我们使用的for each被解语法糖之后就是用迭代器来进行迭代遍历的, 所以在我们使用for each对list中当前的迭代元素进行删除的时候就会抛出异常, 后面会对这个原因进行解析
构造器
// 传入列表的初始大小
public ArrayList(int initialCapacity) {
// list中的元素是通过对象数组的形式储存, elementData就是一个对象数组
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 这里的EMPTY_ELEMENTDATA是被final修饰的一个静态变量, 一个空数组
this.elementData = EMPTY_ELEMENTDATA;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
// 无参构造器, 赋值的也是一个空数组, 后面会根据这个来给初始容量
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 传入一个Collection类型的变量, 上面可以看见ArrayList与Collection是有继承关系的
public ArrayList(Collection<? extends E> c) {
// 直接将c转为数组赋值, toArray返回的是object[]
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
// 这里的copyOf会将elementData进行深拷贝并返回为Object[]类型
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
add
add操作其实就是数组操作, 但是其中含有数组扩容
public boolean add(E e) {
// 判断容量是否足够
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 一个确认容量大小的方法
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
// 这里判断elementData是不是用无参构造器来构造的
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 传入的大小与默认大小10相比, 取大
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
// 这里传入的是calculateCapacity处理后的minCapacity
// add操作无论如何都会进入这个方法, 所以在这里对modCount进行自增
modCount++;
// 不需要扩容的时候minCapacity是一定小于等于数组容量的
// 这里才是对初始数组进行扩容到初始容量10的操作
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// 扩容后的容量为扩容前容量的1.5倍
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 如果扩容后的容量依旧比传入的参数小, 就将扩容后的容量设置为传入参数
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果扩容后容量大于Integer.MAX_VALUE - 8, 依据入参的大小
// 设置扩容后容量为Integer.MAX_VALUE - 8或者Integer.MAX_VALUE
// 这里设置的MAX_ARRAY_SIZE之所以不是Integer.MAX_VALUE是因为List要有8字节存放自身大小
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 新数组将原先的元素浅拷贝至新的扩容后的数组
elementData = Arrays.copyOf(elementData, newCapacity);
}
contains
contains的时间复杂度是O(n), 调用indexOf方法实现的
public boolean contains(Object o) {
// 不存在o就会返回-1, 所以就是false
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
// 如果是null就不能用equals方法来寻找, 所以得判断null的情况
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;
}
// 顺便看看lastIndexOf
public int lastIndexOf(Object o) {
// 与indexOf是一样的, 只是遍历是从后往前的
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;
}
clone
clone是一个浅拷贝, 因为copyOf的拷贝过程也只是引用拷贝, 并不是值拷贝
可以自定义一个对象来进行测试
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
// 这里的copyOf产生了一个新的Object[], 但是其中的元素地址还是不变
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);
}
}
// 可以看见最后的新数组赋值的用本地方法arraycopy实现的
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;
}
public static native void arraycopy(Object src, int srcPos,
Object dest, int destPos,
int length);
get
get方法稍微简单
public E get(int index) {
// 检查下标有无越界
rangeCheck(index);
return elementData(index);
}
// 这里只做了上界判断
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
// 下界判断交给elementData
E elementData(int index) {
return (E) elementData[index];
}
在jdk8中不对负数做判断的解释
/**
* Checks if the given index is in range. If not, throws an appropriate
* runtime exception. This method does not check if the index is
* negative: It is always used immediately prior to an array access,
* which throws an ArrayIndexOutOfBoundsException if index is negative.
*/
可以理解为获取elementData[index]的时候, 如果index是负数那就会直接抛出异常, 这个动作并不是在ArrayList中发起的, 而是jvm抛出的异常
set
public E set(int index, E element) {
// 也只是做上界判断
rangeCheck(index);
// 在这里如果index是负数, 就会直接抛出异常, set失败
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
remove
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
// 这里要对数组进行移动, numMoved是要移动的元素的个数
int numMoved = size - index - 1;
if (numMoved > 0)
// 将从index+1位置开始的NumMoved个元素复制到index开始
// 假设1,2,3,4,5删除2,移动后的数组为1,3,4,5,5
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 最后将数组最后一位设为null, 并且数组拥有的元素个数-1
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
// 这是删除一个对象
public boolean remove(Object o) {
// 也要对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的作用是记录修改了列表结构的操作发生了几次
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
}
// 顺便带个clear, 本质也是删除嘛
public void clear() {
modCount++;
// clear to let GC do its work
// 将数组中所有元素设置为空, 这里设空之后, 原先的元素就失去了引用链, 会被gc回收
for (int i = 0; i < size; i++)
elementData[i] = null;
// 大小归零
size = 0;
}
removeAll
removeAll的作用就是将入参中的元素从this中除去
public boolean removeAll(Collection<?> c) {
// 对c进行一个判空
Objects.requireNonNull(c);
return batchRemove(c, false);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
// 如果元素不存在那么w和r都会+1保持距离, 如果这个元素存在, 那么r就会+1, w保持不变
// 这样就表示当前的w位置是需要被删除的, 可以用其他元素覆盖
// 举个例子:1,2,3,4,5删除1,3,4, 那么后面得到的数组就是2,5,3,4,5
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的情况就是c.contains有异常抛出
// 如果一切正常那么r是一定与size相等的
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
// w==size的情况就是没有对数组进行过移动操作, 也就是c中没有一个元素与this相同
if (w != size) {
// clear to let GC do its work
// 上面的过程可以知道此时w之前的就是删除后的新数组, 之后的都是无用元素
for (int i = w; i < size; i++)
elementData[i] = null;
// 此次一共进行了size-w次的移动操作
modCount += size - w;
size = w;
modified = true;
}
}
// c中如果有元素与this相同就会返回true 否则就是false
return modified;
}
subList
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 + ")");
}
在ArrayList里面有一个内部类SubList, 这个SubList的一切功能都与ArrayList相同
所以这里的SubList其实是不能强制转换为ArrayList的, 他们之间不存在继承关系, 所以我们一般都是使用List来获取ArrayList的SubList的.
这个SubList中的所有元素都与原先的List中的引用地址相同, 所以实质上他们是同一个List, SubList可以理解为一个List中的一个窗口, 而SubList只拥有这个窗口, 他可以修改窗口里面的值
后记
阅读完ArrayList源码之后, 明白了ArrayList的所有操作都是围绕着数组进行的, 其实也与数组无异, 无非就是一个可以动态扩容的数组罢了