刚开始读jdk源码决定从集合开始,只读一些较为常用重要的部分。
首先是属性:
private static final int DEFAULT_CAPACITY = 10;
字面上可以看出其实就是默认的一个容量大小。
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
这两个是用于共享的空数组实例。
transient Object[] elementData;
是用来存放元素的,所以arrayList底层其实就是数组,transient的作用是这个属性不会被序列化。
private int size;
数组列表的大小。
下面是构造函数:
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);
}
}
这个构造函数需要传一个int型参数,通过这个参数来设置arrayList容器的初始化容量大小。
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
不传任何参数就会赋值一个空的数组。
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
它也可以接受一个Collection或者它的子类,首选会把集合参数转为数组存到elementData里,接着判断elementData数组的元素个数是否等于0,等于0就直接赋值一个空的数组,不等于0判断elementData是否是Object[],最后就是复制了。
arrayList.add():
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
首先我们要弄清楚集合的长度和容量的区别,这指的是两个概念。
打个比方,一个电梯可以乘坐10个人,现在里面坐了3个人,那么这个10就指的是容量,而这个3指的是长度。
arrayList添加元素的时候首先会调用ensureCapacityInternal(size+1)方法
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
从字面可以看出这个参数就是最小容量的意思,elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA指的应该就是无参构造函数了,而下面的赋值就可以很明确的知道了为什么会初始化容量为10了。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
modCount是用来记录list结构修改的次数。
minCapacity-elementData.length>0,这个地方我想了很久,查阅资料最后才明白是什么意思,minCapacity指的是数组的元素数量加1,而elementData.length指的是数组的容量。所以如果大于0返回true,说明数组容量已达最大了。接着就需要扩容了。下面是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);
}
这里的代码就比较好理解了,首先是获取当前数组长度赋值为一个老的容量,然后定义一个新的容量是老容量的1.5倍,最后判断新容量是否小于最小容量,小于的话就将最小容量赋值给新的容量,如果新容量大于数组最大容量,也就是
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
那么执行hugeCapacity方法,最后就是将数组和新容量复制到elementData。
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
hugeCapacity中就是对最小容量进行了判断,大于MAX_ARRAY_SIZE就返回int的最大值,否则返回MAX_ARRAY_SIZE,这个一般不会有这么大,就不必深究了。
add(int index, E element)
addAll(Collection<? extends E> c)
addAll(int index, Collection<? extends E> c)
都差不多,第一个add理解了,这三个看一下就明白了。
remove(int index)
remove(Object o)
fastRemove(int index)
这三个方法看完之后发现一个问题,arrayList的源码对数组进行操作基本都是
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
最后看看arraylist的清空集合方法
public void clear() {
modCount++;
// clear to let GC do its work
for (int i = 0; i < size; i++)
elementData[i] = null;
size = 0;
}
其实就是将数组的每个元素设置为空,长度设置为0.
第一次看源码发现没有想象中的那么难,但是我也只是看懂了源码的实现,可能还有很多设计思想需要以后慢慢学习,还需要看更多的源码,继续努力。
补充
1、由于ArrayList是基于数组,且每次删除数据,只是把需要删除的数据之后的数据的下标向前移了一位,这样大量删除数据会导致数组中有很多的空,浪费了空间,所以ArrayList提供了一个方法trimToSize()
,这个方法会把ArrayList 实例的容量调整为列表的当前大小。
2、ArrayList的遍历,建议不要在foreach中对集合进行增删,remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。