Vector是集合当中的一个重要知识点,它于ArrayList相比,最大的区别在于Vector是线程安全的,在源码当中的添加,删除等重要方法当中都加入了关键字 synchronized,实现了操作的线程安全,与ArrayList一样,Vector同样实现了 RandomAccess, Cloneable, Serializable接口。
2.Vector当中的字段
和ArrayList一样,我们先来对看jdk当中对Vevtor字段的定义,这有助于我们理解源码。
首先我们来看vector的定义字段,vector当中的字段不算很多,其中最重要的是理解elementData这个object类型的数组,一定要记住这个elementData是一个数组,而不是元素数据。这是vector乃至集合能够存储任何类型数据的原因,因为其底层是一个object类的数组。
public class Vectorextends AbstractList implements List , RandomAccess, Cloneable, java.io.Serializable{ protected Object[] elementData; //这是真正存数据的一个object类型的数组,这解释了为什么 //vector可以放任何类型的数据。 protected int elementCount; //这里记录vector当中元素的个数,也就是elementDate这个数组当中的数组元素 protected int capacityIncrement; //这是一个在扩容时用来判断的容量增量,默认为0(int类型数据默认为0) private static final long serialVersionUID = -2767605614048989439L; //这是一个序列号版本ID,用来判断类的,不用管他 }
Vector当中的扩容
当我们直接new一个vector时,如果不指定其初始容量大小,其会给一个默认大小为10的容量数组,如果你继续往下追,会发现,最后仍然会走到带参数的一个有参构造,所以结论:vector集合带参不带参最终都会走有参构造,区别是带的参数是你设置的还是jdk给你设置的。
//这是无参构造
public Vector() {
this(10);//给一个初始值,进入有参构造
}
//这是有参构造
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
我们以jdk设置的默认大小为10的容量为例,对其扩容进行分析,为了尽量明了,我们这里就通过以下代码进行剖析:
public class vectorDetail {
public static void main(String[] args) {
Vector vector = new Vector();;
//这里我创建了一个object类型的vector,
//创建时如果不指定类型,默认为object,也就是我们上文说的elementData数组。
for (int i = 0; i <11 ; i++) {
vector.add(i);
}
//因为无参默认大小是10,我这里再加入第11个
//元素(值为10,数组下标是从0开始)时必定会触发扩容机制。
}
我们来具体看看,vector.add()方法到底长什么样?可以看到,add()方法被synchronized修饰着,这就是vector线程安全的根本原因,这里加了锁。此时很显然,我们的容量是不足够的,当前elementData数组空间已经被我们用完了,其elementData[ ]{0,1,2,3,4,5,6,7,8,9}现在是这么一个形式,已经把10大小的容量空间占完了,所以当我们要添加元素10时,必然会触发扩容。
public synchronized boolean add(E e) { //这是我们具体要添加的元素,
modCount++; //这是我们对集合修改的次数,不重要
ensureCapacityHelper(elementCount + 1);
//这是判断我们当前vector集合当中的容量是否足够,
//elementCount加一是我们当前所需要的最小容量
elementData[elementCount++] = e; //将元素赋值给elementData数组
return true;
}
简单来看看ensureCapacityHelper(int minCapacity这个方法的代码,只要我们当前所需要的最小容量比数组的容量大就进行扩容grow()方法。
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
今天的重头戏来了,我们来看看vector集合的扩容到底是如何实现的,可以看到vector底层是将原先的大小进行两倍扩容,然后返回给elementData。在方法内部真正实行扩容的是Arrays当中的一个静态方法。这个方法就是传入你一个原先的数组和一个你需要的空间大小,它帮你生成一个新的具有newCapacity大小的一个数组,然后将你原先数组当中的元素给拷贝到新的数组当中返回,这个方法的实现这里就不多赘述了,有兴趣的小伙伴也可以自己去看看源码。
private void grow(int minCapacity) {
int oldCapacity = elementData.length; //将原先的数组长度赋给oldCapacity
int newCapacity =
oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);
//三目运算符,因为我们的capacityIncrement默认等于0,在vector字段当中我有点出
//因此我们的newCapacity=oldCapacity+oldCapacity,也就是原先长度的两倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity);//这里进行真正的扩容。
}
总结
Vector的扩容是比较简单粗暴的,直接两倍扩容,没有太多的弯弯绕绕和判断,在对线程安全有要求时,我们可以考虑使用vector。



