不知道怎么开始,先看下JAVA的API(1.8)中LIST集合体系的组成:
LIST实现了两个接口,一个是集合类的父接口,一个是迭代器接口;而子类就比较多,有ArrayList、LinkedList、CopyOnWriteList、Vector等,然后就一个个的来深入了解吧!
ArrayList 源码深入解析
先来看构造方法:
第一个构造方法,需要参数 - 初始化容量,那这个容量用来干嘛呢,可以看到源码中是创建了一个大小为参数数值的对象数组;
第二个构造方法,无需参数,直接等于默认容量的空数组,注意上面的注释"构造一个空集合初始化容量是10";
第三个构造方法是传入一个集合,把集合中的元素方法底层数组中;
通过构造方法可以得知,ArrayList 底层应该是用一个对象数组来存储集合中的元素;
/** 默认容量为10 */
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/** 底层存储集合元素的对象数组 */
/**
* 这里有一点补充的是,为什么底层数组要用transient修饰符修饰呢?
* transient修饰符的意思是:该修饰符修饰的变量可以不被序列化,为什么这么做?
* 因为底层数组的大小(length)和集合的元素个数(size)在很多情况下是不相等的,在这种情况下,序列化
* 集合时,如果序列化整个底层数组,就难免会把空的部分也进行序列化,一定程度上浪费空间;而用
* transient修饰符修饰的变量,可以自定义序列化方式,就不会造成不必要的浪费;
* 在ArrayList集合中有自己的序列化方法(writeObject,readObject)以及通过transient修饰符来保证序
* 列化的合理高效
transient Object[] elementData;
挑几个重点先说,之后慢慢填补!!!
-
ArrayList 的扩容机制
在分析扩容机制之前,应该考虑在什么情况下会需要扩容呢?
添加元素:add()
public boolean add(E e) {
/** 扩容流程 */
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
/**
* 所需的最小容量,size+1
*/
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
/** 计算容量 */
private static int calculateCapacity(Object[] elementData, int minCapacity) {
/**
* 计算容量,先判断当前集合底层数组是否为空,如果为空(说明集合第一次添加元素),
* 则选择默认初识化容量10和扩容容量之间的较大值
*/
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
/** 如果不为空,则直接返回扩容容量 */
return minCapacity;
}
/** 判断是否需要扩容 */
private void ensureExplicitCapacity(int minCapacity) {
/**
* 不管需不需要扩容,都要给计数器自增 - 该变量在后面的fail-fast机制中很重要
*/
/**
* 这里还有一点需要说明的是:如果批量添加元素时,modCount只会加1,所有该值不能说和size大小一样
*/
modCount++;
/** 如果扩容容量比当前集合底层数组的长度还大,则说明需要扩容,否则不需要 */
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
/** 扩容关键方法 */
private void grow(int minCapacity) {
// 当前容量
int oldCapacity = elementData.length;
// 扩容1.5倍后的容量大小
int newCapacity = oldCapacity + (oldCapacity >> 1);
/**
* 比较扩容1.5倍后的容量和所必需的最小容量大小,如果所需的最小容量大于1.5倍容量,
* 则扩容为最小容量,否则扩容为1.5倍
*/
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
/**
* 再比较经过上述判断的扩容容量和最大数组大小的关系,如果大于最大数组大小(Integer最大值减8),
* 则扩容容量变为Integer的最大值
*/
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
/** 通过数组拷贝的方法进行扩容 */
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
-
ArrayList 的 fail-fast机制(快速失败机制)
arrayList的快速失败机制(fail-fast机制) - java集合框架中的一种错误检测机制
概念:arrayList的fail-fast机制即在对集合进行遍历或者迭代的时候,不能使用集合方法对集合进行结构上的修改,否则会报conCurrentException异常;
再通俗一点就是,遍历集合时不要增加或者删除集合元素;
为什么会有这样一个机制?
原理分析:
在遍历集合的时候,不管我们使用的普通for-each循环还是使用集合特有的迭代器遍历,其实底层都是使用的迭代器
arrayList实现了自己的迭代器:
private class Itr implements Iterator<E> {
// 遍历元素的游标
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
/**
* 注意这里会把modCount赋值给expectedModCount,而expectedModCount不会再发生改变
*/
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
/** 在遍历获取集合元素时,会进行校验 */
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
// 迭代器内部提供了删除元素的方法,使用该方法不会出现快速失败
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
/** 在遍历获取集合元素时,会进行校验 */
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
// 删除元素之后,会重新把modCount的值赋给expectedModCount
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
/**
* 每次遍历元素时都会校验modCount和expectedModCount的值是否一致,如果不相等,则抛出异常
* 因为expectedModCount的值是在迭代器内部把modCount的值赋予给的,不会改变,所以两个值不相等的情况肯定是modCount发生了改变
* 而结合前面arrayList的扩容部分的内容,可以知道在集合添加元素或者删除元素的时候modCount的值会变
*/
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
还在想,还有啥可以补充的!