一点点蚕食JDK源码(一)ArrayList源码
/**
* Appends the specified element to the end of this list.
*
* @param e
* element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
/**
* 1、对数组容量的控制。若数组实际容量小于10 则不扩容,否则扩容为原来的1.5倍
*/
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
ensureCapacityInternal()方法:
private void ensureCapacityInternal(int minCapacity) {
// 当声明为空实例时,取数组的初始长度为10
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
// 选用Math.max()方法,而没有再次实现一个方法
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
// 是否进行扩容
ensureExplicitCapacity(minCapacity);
}
ensureExplicitCapacity(int minCapacity)方法
private void ensureExplicitCapacity(int minCapacity) {
// 这个在HashMap中也有应用,主要作用是数组内部结构是否有变化(删除/新增等操作,具体参见关于该变量的注释)
modCount++;
// overflow-conscious code
// 是否大于当前的elementData数组的长度,若为true则增长为原来的1.5倍,扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
grow()方法是实现的动态扩容的关键代码,在HashMap中也有应用,可以类比。
/**
* Increases the capacity to ensure that it can hold at least the number of
* elements specified by the minimum capacity argument.
*
* @param minCapacity
* the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
// 获取当前的elementData[] 数组的长度
int oldCapacity = elementData.length;
// 此处用位运算,来完成数组的增加到原来的1.5倍
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);
}
remove()方法:
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
*
* @param index
* the index of the element to be removed
* @return the element that was removed from the list
* @throws IndexOutOfBoundsException
* {@inheritDoc}
*/
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);
// 主动设置为null,强制发生GC
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
至此,阅读了add,remove方法,在实现栈-stack类,模拟stack的pop,push方法很有指导意义,我也是因为在实现栈的过程中,开始读了JDK中ArrayList源码,较为简单。
"还有一个比较有意思的设计是AbstractList中的modCount变量
每次对ArrayList进行修改操作(add/remove这样,get不算),它都会将父类AbstractList中的modCount这个变量自加一次
所以modCount这个变量的意思就是modification count了,表示这个list从new出来到现在,一共经历了多少次modification
如果我们需要遍历某个List,最简单的想法就是去拿这个list的迭代器,然后一路next就行了
但是在调用ArrayList的iterator方法创建迭代器时,迭代器初始化时,会用一个expectedModCount变量会记录下这个ArrayList当前的modCount
然后每次调用迭代器的方法时,都会去比对一下迭代器里的expectedModCount是否等于关联的ArrayList的modCount,如果不等,就抛出一个ConcurrentModificationException
设计目的是,ArrayList与它的迭代器都不是线程安全的,ArrayList的迭代器只能在ArrayList不被修改的情况下才能使用,如果获取迭代器后,ArrayList又被修改(无需并发修改,在同一个线程内修改也行),那么迭代器可能会指向一个异常的位置(位置串了一两位都算是好的,如果ArrayList压缩了,可能会引起数组越界的问题)。从简便的角度来看,直接禁止使用这个迭代器是最容易的。
与之相对的就是concurrent包里的CopyOnWriteArrayList,它的迭代器就不会报ConcurrentModificationException"