在介绍Vector 的时候,人们常说:html
底层实现与 ArrayList 相似,不过Vector 是线程安全的,而ArrayList 不是。
复制代码
那么这句话定义的到底对不对呢?咱们接下来结合上一篇文章进行分析:java
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
复制代码
Vector 是一个矢量队列,它的依赖关系跟 ArrayList 是一致的,所以它具备一下功能:数组
*拓展思考:为何 Vector 的序列化,只重写了 writeObject()方法?安全
细心的朋友若是在查看 vector 的源码后,能够发现,writeObject() 的注释中有这样的说法:bash
This method performs synchronization to ensure the consistency
of the serialized data.
复制代码
看完注释,可能会有一种恍然大悟的感受,Vector 的核心思想不就是 线程安全吗?那么序列化过程确定也要加锁进行操做,才能过说其是线程安全啊。所以,即便没有 elementData 没有使用 transient 进行修饰,仍是须要重写writeObject()。多线程
*拓展思考:与ArrayLit,以及大部分集合类相同,为何继承了 AbstractList 还须要 实现List 接口?dom
有两种说法,你们能够参考一下:ide
一、在StackOverFlow 中:传送门 得票最高的答案的回答者说他问了当初写这段代码的 Josh Bloch,得知这就是一个写法错误。函数
二、Class类的getInterfaces 能够获取到实现的接口,却不能获取到父类实现接口,可是这种操做无心义。
/**
与 ArrayList 中一致,elementData 是用于存储数据的。
*/
protected Object[] elementData;
/**
* The number of valid components in this {@code Vector} object.
与ArrayList 中的size 同样,保存数据的个数
*/
protected int elementCount;
/**
* 设置Vector 的增加系数,若是为空,默认每次扩容2倍。
*
* @serial
*/
protected int capacityIncrement;
// 数组最大值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
复制代码
与ArrayList 中的成员变量相比,Vector 少了两个空数组对象: EMPTY_ELEMENTDATA 和 DEFAULTCAPACITY_EMPTY_ELEMENTDATA
所以,Vector 与 ArrayList 中的第一个不一样点就是,成员变量不一致。
Vector 提供了四种构造函数:
乍一眼看上去,Vector 中提供的构造函数,与ArrayList 中的同样丰富。可是在上一节内容 中分析过 ArrayList 的构造函数后,再来看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;
}
复制代码
代码看上去没有太多的问题,跟咱们平时写的代码同样,只是与ArrayList 中的构造函数相比 缺乏了一种韵味。有兴趣的同窗能够去看一下ArrayList 中的构造函数实现。
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
复制代码
JDK 1.2 以后提出了将Collection 转换成 Vector 的构造函数,实际操做就是经过Arrays.copyOf() 拷贝一份Collection 数组中的内容到Vector 对象中。这里会有可能抛出 NullPointerException。
在构造函数上面的对比:Vector 的构造函数的设计上输于 ArrayList。
Vector 在添加元素的方法上面,比ArrayList 中多了一个方法。Vector 支持的add 方法有:
咱们看一下这个多出来的 addElement(E) 方法 有什么特殊之处:
/**
* Adds the specified component to the end of this vector,
* increasing its size by one. The capacity of this vector is
* increased if its size becomes greater than its capacity.
*
* <p>This method is identical in functionality to the
* {@link #add(Object) add(E)}
* method (which is part of the {@link List} interface).
*
* @param obj the component to be added
*/
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
复制代码
从注释上面来看,这个方法就是 跟 add(E) 方法是有着同样的功能的。所以除了返回数据不一样外,也没什么特殊之处了。
咱们顺着上述代码来进行分析 Vector 中的添加方法。能够看到 Vector 对整个add 方法都上锁了(添加了 synchronized 修饰),其实咱们能够理解,在添加元素的过程主要包括如下几个操做:
为了不多线程状况下,在 ensureCapacityHelper 容量不须要拓展的状况下,其余线程恰好将数组填满了,这时候就会出现 ArrayIndexOutOfBoundsException ,所以对整个方法上锁,就能够避免这种状况出现。
与ArrayList 中对比,确认容器大小这一步骤中,少了 ArrayList#ensureCapacityInternal 这一步骤,主要也是源于 Vector 在构造时,以及建立好默认数组大小,不会出现数组为空的状况。
其次 grow() 方法中:
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
//区别与ArrayList 中的位运算,这里支持自定义增加系数
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);
}
复制代码
Vector 中支持自定义的增加系数,也是它在 add() 方法中为数很少的亮点了。
这部分代码跟ArrayList 中没有太多的差别,主要是抛出的异常有所不一样,ArrayList 中抛出的是IndexOutOfBoundsException。这里则是抛出 ArrayIndexOutOfBoundsException。至于为何须要将操做抽取到 insertElementAt() 这个方法中呢?童鞋们能够进行相关思考。
/**
* @throws ArrayIndexOutOfBoundsException if the index is out of range
* ({@code index < 0 || index > size()})
* @since 1.2
*/
public void add(int index, E element) {
insertElementAt(element, index);
}
public synchronized void insertElementAt(E obj, int index) {
modCount++;
if (index > elementCount) {
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
ensureCapacityHelper(elementCount + 1);
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
elementData[index] = obj;
elementCount++;
}
复制代码
在添加方法上面,Vector 与ArrayList 大同小异。Vector 多了一个奇怪的 addElement(E)。
Vecotr 中提供了比较多的删除方法,可是只要查看一下源码,就能够发现其实大部分都是相同的方法。
对比一下 remove(int location) 和 removeElementAt(int location)
public synchronized void removeElementAt(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
throw new ArrayIndexOutOfBoundsException(index);
}
int j = elementCount - index - 1;
if (j > 0) {
System.arraycopy(elementData, index + 1, elementData, index, j);
}
elementCount--;
elementData[elementCount] = null; /* to let gc do its work */
}
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount)
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index);
int numMoved = elementCount - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--elementCount] = null; // Let gc do its work
return oldValue;
}
复制代码
除了返回的数据类型不一样,其余内部操做实际上是一致的。remove 是重写了父类的操做,而removeElement 则是Vector 中自定义的方法。ArrayList 中提供了 fastRemove() 方法,与其有着一样的效果,不过removeElement 做用范围为public。
public boolean remove(Object o) {
return removeElement(o);
}
public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}
复制代码
remove(Object object) 实际内部调用的就是 removeElement(Object object) 。删除操做首先找到 对象的索引(与ArrayList 中的remmove(E)同样),而后调用removeElementAt(i)(ArrayList 中调用 fastRemove()方法)进行删除。
其他删除操做与ArrayList 相似,这里不作详细解析。整体来讲,在删除方法这一块的话,Vector 与ArrayList 也是大同小异。
拓展思考,咱们常说Vector 是线程安全的数组列表,那么它究竟是不是无时无刻都是线程安全的呢?在StackOverFlow 中有这样一个问题:
Is there any danger, if im using one Vector(java.util.Vector) on my server program when im accessing it from multiple threads only for reading? (myvector .size() .get() ...) For writing im using synchronized methods. Thank you.
其中有一个答案解析的比较详细的:
Vector 中的每个独立方法都是线程安全的,由于它有着 synchronized 进行修饰。可是若是遇到一些比较复杂的操做,而且多个线程须要依靠 vector 进行相关的判断,那么这种时候就不是线程安全的了。
if (vector.size() > 0) {
System.out.println(vector.get(0));
}
复制代码
如上述代码所示,Vector 判断完 size()>0 以后,另外一线程若是同时清空vector 对象,那么这时候就会出现异常。所以,在复合操做的状况下,Vector 并非线程安全的。
本篇文章标题是:百密一疏之Vector,缘由在于,若是咱们没有详细去了解过Vector,或者在面试中,经常会认为Vector 是线程安全的。可是实际上 Vector 只是在每个单一方法操做上是线程安全的。
总结一下与ArrayList 之间的差别:
参考资料: