LinkedList、Verctor部分源码阅读
一、LinkedList
首先来看LinkedList的类声明:
public class LinkedList<E> extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
//······
}
LinkedList的继承树如下:
List本身是一个有序(插入顺序)的集合,可以包含重复的元素,提供了按索引访问的方式,继承自Collection。
List有两个重要的实现类,其一是ArrayList,另外一个就是LinkedList。
LinkedList是一个双链表,在添加、删除元素时,具有比ArrayList更好的性能。但是在get和set时性能弱于ArrayList,这显然是数组和链表的性能特性的区别,所以,我们在LinkedList的类定义中可以看到:
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
transient 关键字:“其实这个关键字的作用很好理解,就是简单的一句话:将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化。”
换言之,当我们需要序列化存储LinkedList时,不需要存储LinkedList的头指针、尾指针、size性。
另外,LinkedList实现了Queue接口,因此,做如下声明即是一个队列(仅支持尾插头出):
//队列
Queue<Integer> q = new LinkedList<>();
q.add(1);
q.remove();
而这样声明,这是一个双端队列。
//双端队列
LinkedList<Integer> l = new LinkedList<>();
l.addFirst(1);
l.addLast(2);
l.add(3);//默认的是和Queue一样进行尾插
l.removeFirst();
l.removeLast();
LinkedList 是线程不安全的。
二、Vector
事先回顾一下ArrayList,ArrayList是一个线程不安全、自动扩容的动态数组,它构造函数的三种参数形式分别是:具体的initialCapicity,不填写(默认为10开始按1.5倍进行扩容),填0(默认为0,从0->1->2->3->4->6进行1.5倍进行扩容),扩容方式是开辟新的更大的数组空间,然后将所有数据移动到新的数组空间。
而Vector则也是基于数组形式实现的,vector本意是向量,vector的类声明如下:
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
protected Object[] elementData;
protected int elementCount;
protected int capacityIncrement;
}
}
Vector同样有多个构造函数,首先是默认构造函数,它默认创建了一个大小为10的Vector。
public Vector() {
this(10);
}
其次是:
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
最后是:
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
这三个构造函数从上到下都会逐次调用。最终初始化的参数包括:elementData、capacityIncrement,后者是单词空间扩容的步长。
Vector的add() 方法簇:
public synchronized boolean add(E e) {
modCount++;
add(e, elementData, elementCount);
return true;
}
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)//末尾插入
elementData = grow();
elementData[s] = e;
elementCount = s + 1;
}
private Object[] grow() {
return grow(elementCount + 1);
}
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
capacityIncrement > 0 ? capacityIncrement : oldCapacity
/* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
}
其中newLength的三个参数分别为oldCapacity、minGrowth、prefGrowth,如果capacityIncrement > 0 ,那就取capacityIncrement ,否则数据空间 * 2,然后复制数组,capacityIncrement是我们构造函数中填入的扩容步长,如果不填默认为0。
Iterator和Enumeration的区别
Enumeration接口中定义了一些方法,通过这些方法可以枚举(一次获得一个)对象集合中的元素。这种传统接口已被迭代器取代。
public Enumeration<E> elements() {
return new Enumeration<E>() {
int count = 0;
public boolean hasMoreElements() {
return count < elementCount;
}
public E nextElement() {
synchronized (Vector.this) {
if (count < elementCount) {
return elementData(count++);
}
}
throw new NoSuchElementException("Vector Enumeration");
}
};
}
该方法用于获得一个Enumeration实例,我们可以重写内部的两个方法hasMoreElements()、nextElement()。实际上和Iterator的hasNext和next相似。
那么,Enumeration有什么作用呢?我们知道,在这类元素集合中,我们不能在迭代时对数据进行插入修改操作。例如:
Iterator iterator = v.iterator();//获取Iterator
while (iterator.hasNext()){
Integer value = (Integer) iterator.next();
System.out.println(value);
if(value == 45){
v.add(10000);//对vector进行差入
}
}
产生报错:Exception in thread "main" java.util.ConcurrentModificationException
对调用栈进行溯源,可以发现,在Vector中实现的Iterator的next方法如下:
public E next() {
synchronized (Vector.this) {
checkForComodification();//报错
int i = cursor;
if (i >= elementCount)
throw new NoSuchElementException();
cursor = i + 1;
return elementData(lastRet = i);
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
而expectedModCount在iterator被创建时,赋值为:
int expectedModCount = modCount;
当我们对数据进行增删,ModCount发生了改变,抛出异常。!
而在LinkedList的Iterator的next()函数定义处,我们可以发现,ArrayList中同样进行了checkForComodification(),但是在操作中没有对数据进行加锁。所以我们对ArrayList迭代时,进行增删也会报出错误。
public boolean hasNext() {
return nextIndex < size;
}
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
}
但是采用for循环遍历一个LinkedList并不会报错,但是采用list.forEach(new Consumer)去遍历同样会报错。
而采用Enumeration进行遍历,则不会产生报错。
Vector<Integer> v = ne w Vector<>();
for (int i = 0; i < 100; i++) {
v.add(i);
}
Enumeration iterator = v.elements();//获取Iterator
while (iterator.hasMoreElements()){
Integer value = (Integer) iterator.nextElement();
System.out.println(value);
if(value == 45){
v.add(10000);
}
}