首先我们再来回顾一下List集合框架的具体体现:
从中可以看出,Vector容器是和ArrayList最相似的集合类容器,其父类都是AbstractList类,那Vector到底有什么用?为什么有了ArrayList还要创一个Vector容器呢?那就从源码开始慢慢解读吧。
首先看一看Vector类的结构:
public class Vector<E> extends AbstractList<E>
implements List<E>,RandomAccess,Cloneable,java.io.Serializable
再对比ArrayList类的结构:
public class ArrayList<E> extends AbstractList<E>
implements List<E>,RandomAccess,Cloneable,java.io.Serializable
发现除了类名,其他的都一模一样,同样都是继承了AbstractList,同样都实现了List接口、RandomAccess接口、Cloneable接口和java.io.Serializable接口,这些接口的功能我在我的另一篇博客都已经讨论过,此处就不再赘述了,具体可移步我的另一篇博客:源码解析——ArrayList。
接下来再看Vector的成员变量和常量:
protected Object[] elementData;
//存储vector实际数据的数组
protected int elementCount;
//存储vector数组的实际长度
protected int capacityIncrement;
//进行相关的增长
private static final long serialVersionUID = -2767605614048989439L;
//验证版本是否一致,用于反序列化
此处和ArrayList相比就少了很多东西了,首先它只有一个成员数组elementData、一个存储vector长度的elementCount、一个用于扩容的变量capacityIncrement还有一个作为反序列化的标识serialVersionUID。除此之外,其成员变量的访问权限几乎都是protected而不是像ArrayList那样的全员private;至于protected和private的区别,如下图:
也就是说上面的那些成员变量除了可以被本类中调用,也可以被本类的子类调用。
接下来再看Vector的构造器:
public Vector(int initialCapacity,int capacityIncrement){
//构造器1,传入参数为数组初始大小以及增长系数...这个增长系数怎么算?
super();
if(initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity:"+ initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
public Vector(int initialCapacity){
//构造器2,传入参数是初始大小,所以增长系数默认为0
this(initialCapacity,0);
}
public Vector(){
//构造器3,传入空参,那么默认构造一个长度为10的数组,增长系数为0
this(10);
}
public Vector(Collection<? extends E> c){
//构造器4,如果传的是一个集合,那将其转为数组存入Vector
elementData = c.toArray();
elementCount = elementData.length;
//如果c.toArray返回的不是Object类型的数组,那就转为Object类型
if(elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData,elementCount,Object[].class);
}
Vector容器有四个构造器,但其实仔细分析,其实和ArrayList的构造器类似,主要分为无参构造器、参数是初始大小的构造器、参数是集合的构造器以及还有一个参数是初始大小和增长系数的构造器。这个增长系数是什么东西?有什么作用??为什么可以在构造器中就可以定义了?从字面上来看,这个增长系数应该是和扩容有关的变量,因此先来看一看Vector的核心方法——扩容方法:
public synchronized void ensureCapacity(int minCapacity){
//确认传入容量参数是否合法
if(minCapacity > 0){
modCount++;
ensureCapacityHelper(minCapacity);
}
}
private void ensureCapacityHelper(int minCapacity){
//确认容量辅助方法,如果最小容量大于当前数组长度,那就增长
if(minCapacity - elementData.length > 0)
grow(minCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity){
//增长方法,和ArrayList类似,不过多了一个capacityIncrement
//ArrayList是首先在原容量上提升1.5倍,此处则先获取增长因素
//如果增长因素设置了,那新容量等于原容量增加增长因素
//否则新容量等于原容量的两倍,再与传入参数进行对比
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0)
? capacityIncrement
: oldCapacity);
if(newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if(newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData,newCapacity);
}
private static int hugeCapacity(int minCapacity){
//如果minCapacity大于int最大值,那就成负数了,所以抛出内存溢出异常
if(minCapacity < 0)
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE)
? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
自此,应该已经看出了一些ArrayList和Vector的区别了。首先,构造方式不太一样,如果传入空参,那么ArrayList会直接初始化一个空数组,等到需要增加元素了,那么再对成员数组进行扩充,第一次扩充直接扩大10容量,而后每一次扩充先扩原容量的1.5倍,再与传入的参数对比,如果传入的扩容量大于系统自己扩的新容量,那么就按照传入的容量来扩,否则就直接扩1.5倍。
而在Vector容器中,如果传入空参,那么会直接初始化一个容量为10的数组,不会创建空数组。并且创建Vector的时候可以直接传入一个增长因素,这个增长系数运用在每次扩容的时候。当需要扩容时,方法首先会去判断用户是否传入增长系数,如果没传入,即成员变量初始化为0,那么就每次扩容都会增加原容量的两倍,如果传入了,那就每次扩容增加一个增长系数。增加后所得到的新容量再与传入的参数对比,决定最后按照哪个容量进行扩容。
由于Vector对象初始化的时候直接就是长度为10的数组,因此扩容时不需要判断是否数组为空,所以步骤少了很多。
那么为什么ArrayList初始化的时候要创建一个空数组而不是直接创建一个长度为10的数组呢?这个问题我在网上检索了很久,也没有找到一个比较满意的答案。那我直接说说我自己的理解吧,算作抛砖引玉。
我认为ArrayList主打的是快速,以牺牲一部分安全性为代价提高查找速度,而Vector主打的是安全性,就算速度慢一些,但是要保证安全性。所以ArrayList一般用于单线程,而Vector多用于多线程。那么ArrayList为了达到快速的目的,如果直接创建其对象而不传入什么参数的话,它就默认创建空数组,因为这样速度快,只有用到它的时候,它再初始化数组,扩容为10。而Vector为了追求安全性,一开始就创建容量为10的数组,一定程度上防止了空指针异常的发生,但是速度可能就慢了一些。
之后就开始上源码了
public class Vector<E> extends AbstractList<E>
implements List<E>,RandomAccess,Cloneable,java.io.Serializable
{
protected Object[] elementData;
//存储vector实际数据的数组
protected int elementCount;
//存储vector数组的实际长度
protected int capacityIncrement;
//进行相关的增长
private static final long serialVersionUID = -2767605614048989439L;
//验证版本是否一致,用于反序列化
public Vector(int initialCapacity,int capacityIncrement){
//构造器1,传入参数为数组初始大小以及增长系数...这个增长系数怎么算?
super();
if(initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity:"
+ initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
public Vector(int initialCapacity){
//构造器2,传入参数是初始大小,所以增长系数默认为0
this(initialCapacity,0);
}
public Vector(){
//构造器3,传入空惨,那么默认构造一个长度为10的数组,增长系数为0
this(10);
}
public Vector(Collection<? extends E> c){
//构造器4,如果传的是一个集合,那将其转为数组存入Vector
elementData = c.toArray();
elementCount = elementData.length;
//如果c.toArray返回的不是Object类型的数组,那就转一下,不是Object那是什么??
if(elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData,elementCount,Object[].class);
}
public synchronized void copyInto(Object[] anArray){
//拷贝,传入一个anArray数组,将Vector中的数组拷贝到该数组中
//添加了关键字synchronized同步,使本方法是同步的,进程进入方法体
//后获得内置锁,其他线程倘若也想进入本方法必须等前一个线程离开方法松开锁
System.arraycopy(elementData, 0, anArray, 0, elementCount);
}
public synchronized void trimToSize(){
//剪枝,将数组长度压缩到vector真实长度,避免空间浪费
modCount++;
int oldCapacity = elementData.length;
if(elementCount < oldCapacity){
elementData = Arrays.copyOf(elementData,elementCount);
}
}
public synchronized void ensureCapacity(int minCapacity){
//确认传入容量参数是否合法
if(minCapacity > 0){
modCount++;
ensureCapacityHelper(minCapacity);
}
}