Vector实现原理源码分析

Posted hymKing

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vector实现原理源码分析相关的知识,希望对你有一定的参考价值。

Vector在jdk1.0版本就存在的一个并发列表类,Vector和ArrayList实现了非常相似的接口,集成了相同的类。和ArrayList不同的是,Vector是线程安全的,大部分的对外暴露的公共方法上,都添加了synchronized同步锁,很显然,同步锁的加入,使得Vector的性能并不高,要使用线程安全的List,则推荐CopyOnWriteArrayList。

一、Vector的类结构

从类的结构图看,Vector的类结构体系基本和ArrayList一致。

二、源码分析

/**
 * 存储元素的object数组
 */
protected Object[] elementData;

/**
 * 元素的个数
 */
protected int elementCount;

/**
 * 容量增量,容量是动态变化的,如果capacityIncrement(=0或者<0)的时候,会以2倍容量进行扩容
 */
protected int capacityIncrement;

Vector可以存放任意类型的元素(包括null),允许重复,和ArrayList基本一致,内部的数据结构,也是Object数组,扩容也是动态扩容机制。

构造函数源码:

/**
 * 根据初始容量、容量增长因子创建一个空的vector对象
 *
 * @param   initialCapacity     初始容量
 * @param   capacityIncrement   容量增长因子
 * @throws IllegalArgumentException 初始容量传了负数的时候,会抛异常
 */
public Vector(int initialCapacity, int capacityIncrement) {
    super();
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    this.elementData = new Object[initialCapacity];
    this.capacityIncrement = capacityIncrement;
}

/**
 * 重载构造函数,默认容量增长因子为0
 */
public Vector(int initialCapacity) {
    this(initialCapacity, 0);
}

/**
 * 重载构造函数,初始化容量为10,默认容量增长因子为0
 */
public Vector() {
    this(10);
}

从源码可以看到基本的三个构造函数,当我们使用new Vector()创建Vector集合的时候,直接创建了一个容量为10的Object数组。对比ArrayList,ArrayList是内部初始化空数组,只有在添加第一个元素的时候才扩容为10,并且capacityIncrement为0,那么容量不足的时候,新数组的容量会扩容为旧的数组容量的2倍。

添加元素的源码:

/**
 * 在Vector的尾部追加新的元素
 *
 * @param e element to be appended to this Vector
 * @return {@code true} (as specified by {@link Collection#add})
 * @since 1.2
 */
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}
/**
  * 是一个私有方法,提供给内部同步的公共方法调用
  *
  * @see #ensureCapacity(int)
  */
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

这里面很简单,添加一个元素的时候,传入的minCapacity是elementCount + 1,就是在现有的数组的实际元素个数上面加1,判定当前的数组大小是否能够容纳,不能容纳则执行扩容计划,调用grow(minCapacity)方法,源码如下:

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;//旧的容量
    //如果构造的时候,明确指定了容量增长因子,则按照容量增长因子增长,否则增加一倍旧的容量,即扩容为2倍
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    //新的扩容量够用了,就是使用新的扩容量,即旧的容量的2倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //新的扩容量比最大值还大,就是使用整形的最大值作为容量
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    //数组拷贝的方式进行赋值操作
    elementData = Arrays.copyOf(elementData, newCapacity);
}

对照一下ArrayList源码中的grow方法如下:

private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);//增长为1.5倍
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

从上述代码,可以看出,grow方法的方法体内容基本和ArrayList是一致的,唯一的区别就是在于扩容的算法上。

其它对于vector的操作方法基本和arrayList一致:

public synchronized E get(int index) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    return elementData(index);
}
public synchronized E set(int index, E element) {
     if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
}
public synchronized boolean removeElement(Object obj) {
        modCount++;
        int i = indexOf(obj);
        if (i >= 0) {
            removeElementAt(i);
            return true;
        }
        return false;
}

区别在于这些对数组操作的方法都加了synchronized关键字,对方法施加了同步锁。当然Vector是线程安全的动态数组,但是线程的安全实现手段是在所有的操作数组的方法上加上了内置同步锁,当有多个线程对Vector执行操作的时候,实际的运行机制类似于多个线程串行化执行,这种方法的效率会很低,在java1.5之后,推出了CopyOnWriteArrayList写时复制的动态数组对象,效率会高很多,推荐使用。

以上是关于Vector实现原理源码分析的主要内容,如果未能解决你的问题,请参考以下文章

Vector实现原理源码分析

JDK源码分析-Vector

CopyOnWriteArrayList实现原理源码分析

CopyOnWriteArrayList实现原理源码分析

CopyOnWriteArrayList实现原理源码分析

《Docker 源码分析》全球首发啦!