今天我们就来分析下ArrayList集合类的源码了,希望可以帮助到你,首先我们还是从集合的入口慢慢深入分析吧。还是一贯风格,看代码咯。
List<String> stringList=new ArrayList<>();
我们先直接new一个集合对象,接下来我们通过IDE工具直接进去代码
通过new ArrayList(),我们跟进了这样的代码。
Tips:左右滑动可以看完整代码信息。
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
上面的方法就是创建一个空集合,所以在new ArrayList()的时候就帮我们创建了一个空数组,我们还是看下elementData是如何定义的。
transient Object[] elementData; //这是一个Object[]数组用于存储任何类型的数据
好了,下面我们继续看下,我们在使用集合的add()方法时是个怎么样的过程吧,还是直接看程序代码。
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
上面的add()方法,就是将需要添加的元素e,添加到集合的末尾,也就是数组的末尾了,不过在看下面的流程之前,我们还是先分析一下ensureCapacityInternal()这个方法。
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
private static final int DEFAULT_CAPACITY = 10;//默认的数组容量
这两个方法主要是判断传入的size大小与默认值10进行比较,获取两者之中的最大值,然后调用ensureExplicaitCapacity()方法。
接下来我们可以ensureExplicaitCapacity()方法的代码主要做了什么。代码继续。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
上面的方法实现的是对传入的值和数组的length进行比较,满足条件直接进入grow()这个方法了,接下来我们继续看下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);
}
这个方法的功能就是,增加容量,以确保它至少可以容纳最小容量参数指定的元素,然后调用Arrays.copyOf()进行拷贝。其实我们再往下面进行代码的分析,就是下面这个方法的调用的。
Tips:左右滑动可以看完整代码信息。
System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));
public static native void arraycopy(Object src, int srcPos,Object dest, int destPos,int length);
由于arraycopy是由native关键字修饰的,它是一个本地方法,而不是一个java方法,所以它是运行在本地方法栈的,而不是java栈,建议先进行了解下面的文章java内存区域划分详解好区分本地方法栈和java栈的区别。
上面的就是调用ArrayList类的add()方法的整个流程了,下面我们再看看其他方法吧。
先看下isEmpty()方法的代码。这个比较简单理解,代码看上去,一目了然。
public boolean isEmpty() {
return size == 0; //判断size大小是否为0,是不是和你写的代码一样,坏笑
}
下面继续分析其他方法吧,既然说到今天分享的是ArrayList类的代码分析,不能草草了事对不,也是自己公众号的初衷。
我们调用集合的size()方法就对应着下面的方法,这个size就是数组里面的元素个数大小。
public int size() {
return size;
}
看下集合是否包含某个元素的contains()方法咯。
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
在这里我们看下indexOf()方法做了什么事情。
public int indexOf(Object o) {
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;
}
上面的indexOf()方法,主要是对传入的对象obj分两种情况进行循环遍历判断,因为add()方法可以添加null值进去的,所以数组里面的元素可能就包含null值。
这时就需要我们按照两种方式,一个是对null值判断,另外一个是正常的元素值,循环遍历整个数组进行一一比较,返回对应的数组的下标值,如果两种遍历都没有找到就返回-1,所以contains()方法调用indexOf()方法就是对返回的数组下标进行了判断。
接下来我们还是慢慢分析吧,由于集合还是比较重要的,需要一点耐心。
我们看下集合的clear()方法的代码。
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
clear()方法的功能是循环整个集合(数组)的大小,将每个集合元素都赋值为null,size赋值为0,这样就让JVM的GC机制来帮我们进行无用对象实例进行清除了,建议先移步到这java虚拟机,应该了解一点点先了解一下什么是JVM,本来不打算写下GC了,看来还是有必要的,后面构思一下,再写咯。
喜欢文章的可以关注,转发和分享一下文章,接下来我们看下集合的addAll()方法吧。
Tips:左右滑动可以看完整代码信息。
public boolean addAll(Collection<? extends E> c) {
Object[] a = c.toArray();//先将集合c转换为数据
int numNew = a.length;
//和上面的代码分析一样
ensureCapacityInternal(size + numNew); // Increments modCount
System.arraycopy(a, 0, elementData, size, numNew);//进行数组元素的拷贝
size += numNew;
return numNew != 0;
}
ok,addAll()方法的功能解释已在代码对应位置说明了。
继续看下根据索引值获取对应元素的get()方法的代码吧,方法的功能和实现在代码中已说明,看注释信息就行了。
public E get(int index) {
rangeCheck(index);//这个就是我们常用的入参校验,我们看下它做了什么
return elementData(index);//这个主要是返回数组对应下标的元素值
}
private void rangeCheck(int index) {
//判断索引下标是否大于size值,若大于直接抛出索引越界异常信息,或许你遇到过这样的异常信息
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
既然数组可以转为集合,那么集合也可以转为数组,我们继续看下集合是如何转换为数组的方法toArray()方法。
public Object[] toArray() {
return Arrays.copyOf(elementData, size);
}
public static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
我们继续看下Arrays.copyOf()方法,这里面涉及到静态工厂方法的内容,建立先看这部分内容java的静态工厂方法,先自己了解下。
其实下面调用的是个静态方法,进行数组之间的拷贝,返回数组。
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;
}
集合的返回集合元素下标的indexOf()方法在上面的分析过程中已讲解。
上面我们说了indexOf()方法,接下来我们看下集合的lastIndexOf()方法。
public int lastIndexOf(Object o) {
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;
}
是不是和indexOf()方法的写法很像,一个是从前往后遍历,一个是从后往前遍历进行判断,找不到就返回-1,同样的套路操作,希望可以帮助到你。
下面我们再看最后一个集合的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;
}
集合的删除方法和indexOf()方法等都一样,都需要区分null值和非null值的,不过这里面调用了共同的fastRemove()方法,我们继续看下fastRemove()方法。
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; //这里将对应的数组元素设置为null,让JVM的GC去清除无用的数组元素,进行内存空间的回收
}
好了,关于ArrayList集合类的源码分析到这里就结束了,希望这篇文章可以帮助到你