本文介绍Vector容器在Java中的底层实现,首先看一下这个类的声明:
public class Vector
extends AbstractList
implements List, RandomAccess, Cloneable, java.io.Serializable
这个类继承了AbstractList
这个类实现了List
实现了RandomAccess接口,通常在对List进行遍历操作的时候我们最好判断这个List是不是实现了RandomAccess接口,如果实现了就尽量用for(int i = 0;i
if(list instanceof RandomAccess){
for(int i = 0;i iterator = list.iterator();
while(iterator.hasNext()){
//遍历操作
}
}
实现了Cloneable接口,表示该对象是可以被克隆的,能够使用Object.clone()方法,如果没有实现这个接口就调用Object下的clone()方法就会抛出CloneNotSupportedException异常。克隆分为浅克隆和深克隆,浅克隆只拷贝基本数据类型而不拷贝引用指向的对象,而深克隆不仅拷贝对象本身,还拷贝对象包含的引用指向的对象。
实现了java.io.Serializable表明这个类可以被序列化。
这个类中的成员变量和构造函数如下:
protected Object[] elementData;//存放元素的数组 也就是vector底层是数组
protected int elementCount;//记录存放的元素个数
protected int capacityIncrement;//增长因子 和增长容量相关 下面会介绍
private static final long serialVersionUID = -2767605614048989439L;//序列版本号
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
public Vector() {
this(10);
}
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);
}
从上面可以看出默认的初始容量是10,而默认加载因子是0。
下面来看一下Vector下面的addElement()方法:
public synchronized void addElement(E obj) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = obj;
}
可以看到实现方式为modCount加1,表示修改次数加了一次,这个字段的作用体现在了快速失败机制中,而所谓快速失败机制就是如果在迭代的过程中,这个Vector被修改了,就不冒险继续迭代,而是直接抛出ConcurrentModificationException异常结束。然后调用了一个ensureCapacityHelper(elementCount+1)以确保容量足够,最后将obj加入到存放元素的数组。整个方法是用synchronized关键字修饰的,所以是线程安全的。
看看ensureCapacityHelper()方法发生了什么:
private void ensureCapacityHelper(int minCapacity) {
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
如果所需最小容量比当前存放的数组容量还要大,就调用grow()方法进行扩容,由于执行这个方法的时候已经是在synchronized方法内,所以这个ensureCapacityHelper()方法没有用synchronized关键字修饰。
看看grow()方法:
private void grow(int minCapacity) {
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);
}
源码写得很清楚,如果capacityIncrement大于0,newCapacity为capacityIncrement+oldCapacity,否则直接等于oldCapacity的两倍。如果容量仍然不够,newCapacity直接等于所需最小容量。如果newCapacity这个时候比MAX_ARRAY_SIZE还大,也就是比Integer.MAX_VALUE-8还要大就通过hugeCapacity(minCapacity)方法返回一个合适的容量,作为newCapacity,最后使用Arrays.copyOf(elementData,newCapacity)方法来实现数组的扩容。
hugeCapacity()方法如下:
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
可以看到当所需最小容量大于MAX_ARRAY_SIZE的时候就取Integer.MAX_VALUE,否则取MAX_ARRAY_SIZE也就是Integer.MAX_SIZE-8。
看一下Vector中的removeElement(Object obj)方法:
public synchronized boolean removeElement(Object obj) {
modCount++;
int i = indexOf(obj);
if (i >= 0) {
removeElementAt(i);
return true;
}
return false;
}
通过indexOf(obj)方法查找obj的索引i,然后调用removeElementAt(i)进行删除。
我们直接看removeElementAt(int i)方法:
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 */
}
删除就没有添加那么麻烦,只是判断一下索引的合法性,然后就使用System.arraycopy()方法进行删除了,注意这里有个小细节:elementData[elementCount] = null,如果没有这行代码会导致elementData[elementCount]这个元素虽然不会再被使用了,但是由于这个元素仍然被elementData这个数组维护着,所以导致这个元素无法被GC回收,当这种情况出现的次数太多,就有可能导致OOM。
总结一下Vector这个容器:
1、这是一个集合,底层是用数组实现的,不过是可变长的,默认初始容量是10,默认增长因子是0,如果想要加入新的元素而容量不足就需要进行扩容,如果增长因子大于0,就增长负载因子个数的容量,否则增长为原来容量的两倍,如果容量仍然不够,就直接增长为所需最小容量。频繁地扩容容易引起效率问题,所以最好在调用构造函数的时候指定一个合适的容量或者调用ensureCapacity()方法进行扩容到适当的容量。
2、这个类是线程安全的,采用了快速失败机制,提供了增加、删除元素,更加方便快捷。
3、线程安全不意味着对于这个容器的任何操作都是线程安全的,比如在进行迭代的时候,如果不增加一些代码保证其线程安全,其他线程是可以对这个容器做出修改的,这样也就会导致抛出ConcurrentModificationException异常。