List实现了Collection接口,产生三个子类:ArrayList,LinkedList,Vector
文章包含解释方面:
数据结构方面,
源码注释方面&迭代器快速失败机制
基于源码的数据结构,我们先给出如下比较结果:
ArrayList&Vector是基于数组的实现。
那么他们:
-基于数组下标的快速查找功能
-数据插入费力,因为要移动一大段数据为新数据提供插入位
-数组可能溢出(已经被开发者利用grow()扩容解决)
必须是一片连续内存空间
注解:
我们看一下ArrayList的扩容源码(Vector省略(扩容一倍)):
判断是否扩容代码,在发生在每次添加数据时(每次使用size+数据长度和当前容量比较),但是源码里面规定了当数组容量小于10(DEFAULT_CAPACITY )时不会进行扩容
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);
}
我们发现扩容很简单,但是也是耗费系统资源的,协调用法比较重要
int newCapacity = oldCapacity + (oldCapacity >> 1);
elementData = Arrays.copyOf(elementData, newCapacity);
这两句说,
当发生扩容时先对原有的数组长度进行右移一位,也就是扩容一半(减少扩容次数,增大用量 )
LinkedList基于双向链表的实现。
那么:
-读取数据效率低一些,因为是双向链表,每次查询都得从头|尾进行遍历节点
-插入效率高些,不管前中后,插入都快,因为他不需要移动大量数据
-当数据过多时,节点(Node对象)本身也是一种资源消耗,数组实现不会
-是一个不连续内存空间
我们看出前者适合多查询,少插入的数据,后者适合多插入,少查询的数据
不过一般大家都是综合考虑。
基于各个源码的类的注释
给出LinkedList:
阅读发现,LinkedList是线程不安全的,它的迭代器是快速失败的
他允节点为null值,遍历都是从头或尾开始的
ArrayList:
阅读发现,ArrayList是线程不安全的,它的迭代器是快速失败的
它是大小可变的,复杂度为O(n)
Vector:
阅读发现,Vector是线程安全的(那消耗资源也会多),迭代器也是快速失败的。
**对于ArrayList,LinkedList的线程不安全,可在创建时利用Collections.synchronizedList进行"包装"
即:
List list=Collections.synchronizedList(new LinkedList());
在此时,我们三个类一起看,发现他们的类,迭代器都是快速失败的,
什么是快速失败?
这是一种集合的错误处理机制(Map,Set也有),他会尽全力抛出ConcurrentModificationException异常(因为判断抛出的标准是值是否相等,而开发者并不保证值被人为修改),在你通过自身迭代器遍历时,对集合内容修改的时候,就会直接抛异常,退出迭代
其实原因很简单,源码里面有个modCount变量,他会在集合迭代时,每次增删时加一(其他非迭代修改没问题),迭代器会通过这个变量是否改变( modCount!=expectedmodCount)进行决定抛异常(在每次迭代下一个元素时next()会触发)
这是一种判断方式,这样会保护集合的正确性,也既是保证集合不能并发访问。
如果还是不对,可以百度:
java中的fail-fast(快速失败)机制
这样再一看List发现再也不是只知形不知意了。
ps:源码学习目前只能靠边看代码边写博客进行总结,没什么更多的有效方法,朋友们可以的话,留下你宝贵的学习 建议|方法,我一定虚心接受。