java中Vector的实现

本文介绍Vector容器在Java中的底层实现,首先看一下这个类的声明:

public class Vector
    extends AbstractList
    implements List, RandomAccess, Cloneable, java.io.Serializable

这个类继承了AbstractList这个抽象类,这个抽象类实现了List接口,实现了一些基本的方法,并且在add,remove,set方法中直接抛出了UnsupportedOperationException异常,这就意味着如果子类直接继承这个AbstractList然后不重写就直接使用这写方法就会抛出UnsupportedOperationException异常。


这个类实现了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 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异常。

你可能感兴趣的:(数据结构)