java中Vector的实现

来源:互联网 发布:mac流程图制作软件 编辑:程序博客网 时间:2024/05/16 19:31

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

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

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


这个类实现了List<E>接口,这个接口不用说了,约定了一些列表下面的方法。

实现了RandomAccess接口,通常在对List进行遍历操作的时候我们最好判断这个List是不是实现了RandomAccess接口,如果实现了就尽量用for(int i = 0;i<size;i++)的形式来进行遍历,效率会高一点,否则就用Iterator迭代器来进行遍历,代码实现如下:

if(list instanceof RandomAccess){    for(int i = 0;i<size;i++){        //遍历操作    }}else{    Iterator<?> 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异常。

原创粉丝点击