List
ArrayList
List就是一个动态的数组 如何动态的扩容当容量达到一定阈值的时候会新建一个大容量的数组然后把旧的数组的数据copy到新的数组哪里去。需要注意的new ArrayList如果不带初始化容量的情况 new ArrayList()的时候默认是空的一个数组。只有在操作add的时候才会去初始化一个容量默认为10的数组。
modCount 这个需要注意下 我们知道 java.util.HashMap 不是线程安全的,因此如果在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略。这一策略在源码中的实现是通过modCount域,modCount 顾名思义就是修改次数,对HashMap内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。在迭代过程中,判断modCount 跟 expectedModCount是否相等,如果不相等就表示已经有其他线程修改了 Map:注意到 modCount声明为 volatile,保证线程之间修改的可见性。 modCount到底是干什么的呢 所以使用在循环中删除同一个list的元素的时候需要好好对待,分分给你抛异常,尽量使用迭代器吧。
// ArrayList扩容的代码
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);
}
foreach中删除异常出现错误的原因(modCount的原因)
foreach只是java语言的语法糖,本质上还是由Iterator来迭代的,但在删除时,调用的是list的remove方法,正是这里引发了修改异常。ArrayList小结:在使用arrayList的时候如果可以大概知道数组的长度返回建议指定大小,避免数组copy的消耗。
CopyOnWriteArrayList
CopyOnWriteArrayList出来的原因就是一个线程安全的一个ArrayList,
只有作为共享list的时候才考虑使用,所有安全的东西是比较慢的。线程安全的原理直接贴代码其实根据它的名字大概也可以猜测出多少东西复制然后再写入的一个ArrayList(取名很优秀)add的方法都要上一把锁ReentrantLock锁整个过程了,这个代码很简单易懂,优秀。
public boolean add(E e) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 获取当前的数组
Object[] elements = getArray();
// 获取当前数组的长度
int len = elements.length;
// 把当前数组的所有元素复制到一个新的数组里面去然后操作
Object[] newElements = Arrays.copyOf(elements, len + 1);
// 把最后一个位置给要添加的元素
newElements[len] = e;
// 把当前对象的数据指向新的数组
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
CopyOnWriteArrayList小结:就是写入操作的时候加锁具体看代码。
Vector
这个玩意跟ArrayList就是一个东西来的只是加了synchronized 全部都加了 get add 都加了 重的很一般不用这个玩意。
附录
接口名称 | 作用 |
List | list容器接口 |
RandomAccess | 支持随机访问 |
Cloneable | 支持克隆 |
java.io.Serializable | 支持序列化 |
参考文献
java集合遍历的几种方式总结及比较modCount到底是干什么的呢