jdk源码分析之 Vector

本文详细介绍了Java中的Vector类,它是一个可扩展的数组,提供了同步访问保证线程安全。然而,由于其同步机制,性能通常低于ArrayList。文章讨论了Vector的初始化、增删查改操作,特别是扩容机制,并提醒在不需要线程安全时使用ArrayList代替。此外,还提到了一个关于toArray方法的JDK bug及其修正。
摘要由CSDN通过智能技术生成

Vector源码的类注解:

  • Vector类实现了一个可扩展的数组对象。像数组一样,它包含可以使用整数索引访问。Vector 是同步访问,的。Vector 包含了许多传统的方法,这些方法不属于集合框架。
  • Vector 主要用在事先不知道数组的大小,或者只是需要一个可以改变大小的数组的情况。
  • 如果迭代时,数据发生了改变会,会快速的失败,抛出ConcurrentModificationException异常。
  • Vector是线程安全的,可以放心使用,但是在不考虑线程安全的问题时,建议使用ArrayList替代.

Vector的成员变量:

/**
     * Vector所在的数组缓冲区已存储
     * Vector中最后一个元素之后的任何数组元素均为null
     */
    protected Object[] elementData;

    /**
     *elementCount是Vector 对象中有效组件的数量。
     */
    protected int elementCount;

    /**
     * Vector容量自动增加的量
     */
    protected int capacityIncrement;

Vector初始化

  • 传入带参的intialCapacity和capacity两个参数的初始化:
public Vector(int initialCapacity, int capacityIncrement) {
        super();
        // 校验intialCapacity数据合法性
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                            initialCapacity);
        this.elementData = new Object[initialCapacity];
        this.capacityIncrement = capacityIncrement;
    }
  • 传入intialCapacity的初始化方式:
 public Vector(int initialCapacity) {
        // 传入一个acpacityIncrement 为的0.
        this(initialCapacity, 0);
    }
  • 无参构造初始化:
public Vector() {
       // 此时initialCapacity 为默认值,而capacityIncrement为0.
        this(10);
    }
  • 带初始数据的初始化:
  public Vector(Collection<? extends E> c) {
        elementData = c.toArray();
        elementCount = elementData.length;
        // c.toArray可能(不正确)不返回Object [] (请参阅6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
    }

其中see 6260652这是jdk的一个bug,意思为当给定的一个集合的元素不是Object类型时,我们将其转化为Object类型一般情况而言不会触发此bug,Vector源码已经经过修改不会触发此bug,注: 只有在下列场景下才会触发:Vector初始化之后(Vector元素非 Object 类型),再次调用 toArray 方法,得到 Object 数组,并且往 Object 数组赋值时,才会触发此 bug,

 public static void main(String[] args) {
        Vector<String> vector = new Vector<>();
        vector.add("vector");
        System.out.println(vector.getClass().getSimpleName());
        Object[] array = vector.toArray();
        System.out.println(array.getClass().getSimpleName());
        array[0] = new Object();
    }

在这里插入图片描述

正常输出,因为源码中已经做了修改。

Vector新增方法
新增的过层大致流程如下:

  • 记录数组改变次数
  • 检测数组的长度是否够用,不够用则进行扩容
  • 直接把所需的添加元素添加到数组的后面
  • 如果上诉几个步骤都没有抛出异常或者error表明已成功添加到数组中,返回true。
 public synchronized boolean add(E e) {
      //  用于记录数组的结构是否变动了
        modCount++;
        // j 检查数组的容量是否足够,不足过时进行扩容
        ensureCapacityHelper(elementCount + 1);
        // 追加到数组后面。
        elementData[elementCount++] = e;
        // 成功后返回true'
        return true;
    }

private void ensureCapacityHelper(int minCapacity) {
        // 有溢出意识的代码,数组的长度是否足够添加,不满足则进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

// 具体扩容过程
 private void grow(int minCapacity) {
        //  原有数组的长度
        int oldCapacity = elementData.length;
        // 如果初始化Vector的时候传入了一个capacityIncrement 则使用新数组的长度为老数组的长度 + capacityIncrement ,反之2×老的数组的长度
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
         //  如果计算出新的数组的所需的长度小于传过来期望值,则使用传过来的期望值作为新数组所需的长度                                
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
            // 如果大于 Integer.MAX_VALUE - 8 则继续调用hugeCapacity()方法,重新计算新数组
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //  根据计算出新数组的长度并将老数组拷贝到新的数组中去,并重新赋值给elementData数组
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

private static int hugeCapacity(int minCapacity) {
      // 如果最小的容量小于0则内存溢出
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
            // minCapacity 大于nteger.MAX_VALUE - 8  则返回Integer最大值,否则返回Integer.MAX_VALUE(最大值)
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

Vector的取值(get)方法
get方法大体流程:

  • 检查传入的索引的合法性,
  • 合法之后直接根据索引从数组elementData获取数据

源码如下:

 public synchronized E get(int index) {
        // 检查索引的合法性 主要是否超出数组的长度
        if (index >= elementCount)
            throw new ArrayIndexOutOfBoundsException(index);
        return elementData(index);
    }

// 根据索引从数组取出数据
E elementData(int index) {
        return (E) elementData[index];
    }

Vector的删除(remove)方法
remove方法大体流程:

  • 检查要删除的索引是否合法
  • 合法后通过从数组中直接取出待删除元素的将其存为一个临时变量存起来
  • 计算出要删除个数,并将要待删除数据往前移动,把数组最后一个置为null.
  • 返回要删除元素的值。
 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);
        // 将数组的最后一个置为null                     
        elementData[--elementCount] = null; // Let gc do its work
        // 返回要删除的元素
        return oldValue;
    }

总结:

  • Vector整体的架构就是数组 + synchronized锁实现的,每个操作到数组的方法都加了synchrony修饰保证其线程安全。
  • 由于是通过同步锁实现的,性能在大多数情况是不如ArrayList好,在不靠线程安全问题的建议使用ArrayList代替。
  • 涉及到新增或者删除批量数据直接只用addAll()或者removeAll(),避免数组频繁扩容或者数组搬移。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值