深入理解Vector

更新时间:2019-12-06 13:49:47 点击次数:1398次
什么是Vector?
Vector是一个可以实现自动增长的对象数组(简称动态数组),可以随着向量元素的增加而动态地增长,实际上是一种动态顺序表的应用

Vector与ArrayList有什么区别?
相同点
Vector:

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

ArrayList:

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

可以看到 Vector 和 ArrayList 都继承了 AbstractList 类,并且都实现了 List、RandomAccess、Cloneable、Serializable 接口,都可以存储 null 值

不同点
这样看来 Vector 和 ArrayLis 的功能是一样的,那为什么会有 Vector 这个动态数组呢?ArrayList不也是动态数组吗?因为 Vector 是基于线程安全的,而ArrayList不是线程安全的,所以有很多人都会说 Vector 是线程安全的 ArrayList

ArrayList默认的扩容倍数是1.5倍(关于ArrayList的扩容可以看下这篇文章 深入理解ArrayList),而Vector默认扩容的倍数是2倍

Vector中的一些成员变量
// 序列ID
private static final long serialVersionUID = 8683452581122892189L;

// 默认初始容量
private static final int DEFAULT_CAPACITY = 10;

// 用于空实例的共享空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};

// 共享的空数组实例,用于默认大小的空实例
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 存储ArrayList元素的数组缓冲区
transient Object[] elementData; // non-private to simplify nested class access

// ArrayList的大小(其中包含的元素数)
private int size;

Vector中的构造方法有哪些?
一、Vector(int initialCapacity, int capacityIncrement)
// 创建输入参数为初始化容量和增长量参数的构造方法
public Vector(int initialCapacity, int capacityIncrement) {
    super();
    
    // 如果向量的初始化容量为负数,则抛出 IllegalArgumentException 异常
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    
    // 将初始化容量的长度作为Object数组(elementData)的实际长度
    this.elementData = new Object[initialCapacity];
    
    // 初始化增量参数
    this.capacityIncrement = capacityIncrement;
}

二、Vector(int initialCapacity)
// 创建一个初始化容量且增量为0的数组
public Vector(int initialCapacity) {
    this(initialCapacity, 0);
}

三、Vector()
// 创建一个初始化容量为10,且增量为0的空数组
public Vector() {
    this(10);
}

四、Vector(Collection<? extends E> c)
// 创建一个包含指定集合中元素的数组,这些元素会按集合的迭代器返回元素的顺序排列
public Vector(Collection<? extends E> c) {
    
    // 将集合元素(c)转换为数组并赋值给Object数组(elementData)
    elementData = c.toArray();
    
    // 获取数组的长度
    elementCount = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    if (elementData.getClass() != Object[].class)
        // 将c中的元素拷贝到Object数组(elementData)
        elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}

Vector的扩容
grow
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

// 传入一个最小容量
private void grow(int minCapacity) {
    // overflow-conscious code
    
    // oldCapacity翻译过来就是旧的容量,把Object数组(elementData)的长度赋值给旧的容量
    int oldCapacity = elementData.length;
    
    // newCapacity翻译过来就是新的容量,当增量 > 0 
    // 那么新的容量就等于旧的容量+增量,否则为旧的容量的2倍(这里跟ArrayList中的不太一样)
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);
    
    // 如果新的容量 - 实际需要的容量 < 0
    if (newCapacity - minCapacity < 0)
        // 将实际需要的容量赋值给新的容量
        newCapacity = minCapacity;
    
    // 如果新的容量比数组最大的容量还要大
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        // 那么就扩容为巨大容量
        newCapacity = hugeCapacity(minCapacity);
    // 用Arrays.copyOf复制
    elementData = Arrays.copyOf(elementData, newCapacity);
}

hugeCapacity()
巨大容量 hugeCapacity() 方法源码如下(跟ArrayList的一样):

private static int hugeCapacity(int minCapacity) {
    
    // 如果传入的实际容量 < 0,则抛出 OutOfMemoryError 异常
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
    MAX_ARRAY_SIZE;
}

为了能更好的理解,我们把这段代码搬过来运行测试一下:

/**
  * 自己新建的类:测试 hugeCapacity 方法的作用
  */
public class HashDemo {

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
                Integer.MAX_VALUE :
                MAX_ARRAY_SIZE;
    }

    public static void main(String[] args) {
        // 假设这里传入10
        int i = hugeCapacity(10);
        System.out.println("Integer Max Value is " + Integer.MAX_VALUE);
        System.out.println("hugeCapacity return is " + i);
    }
}

输出结果为:

可以看到 Integer的最大值为 2147483647,最大值 - 8 = 2147483639,也就是说不管我们传入的数字是多少,经过 hugeCapacity() 运算后得到的结果都是 最大值 - 8 = 2147483639

Integer Max Value is 2147483647
hugeCapacity return is 2147483639

我们再把传入的数字改大一点,比如我们传入一个比 最大值 - 8 = 2147483639 还要大的数字

public static void main(String[] args) {
    // 假设这里传入10
    int i = hugeCapacity(2147483640);
    System.out.println("Integer Max Value is " + Integer.MAX_VALUE);
    System.out.println("hugeCapacity return is " + i);
}

输出结果为:

可以看到当穿传入的数字比 最大值 - 8 = 2147483639 还要大的数字的时候,返回的值就是 Integer 的最大值

Integer Max Value is 2147483647
hugeCapacity return is 2147483647

这样讲下来应该比较好理解了

Vector中常用的操作
add()
// 采用同步的方式往向量中添加元素
public synchronized boolean add(E e) {
    modCount++;
     
    // 因为添加的元素需要动态地扩容,所以调用 ensureCapacityHelper 方法的时候需要 + 1
    ensureCapacityHelper(elementCount + 1);
    // 采用顺序表添加元素,赋值长度+1
    elementData[elementCount++] = e;
    return true;
}

ensureCapacityHelper()
private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
    // 如果elementCount + 1 - 数组的容量 > 0 
    if (minCapacity - elementData.length > 0)
        // 则进行扩容操作(上面已讲解grow方法)
        grow(minCapacity);
}

get()
// 返回向量中指定位置的元素
public synchronized E get(int index) {
    // 如果下标 > 数组元素,则抛出 ArrayIndexOutOfBoundsException 异常
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);

    // 否则返回指定索引的对象
    return elementData(index);
}

remove()
protected transient int modCount = 0;

public synchronized E remove(int index) {
    
    // 修改次数+1
    modCount++;
    
    // 如果下标大于元素的数量,则抛出 ArrayIndexOutOfBoundsException 异常
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);
    
    // 使用下标获取对应的旧值
    E oldValue = elementData(index);

    // 计算删除元素后需要移动补位的元素的数量
    int numMoved = elementCount - index - 1;
    
    // 如果这个数量 > 0
    if (numMoved > 0)
        // 拷贝元素
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    // 删除元素
    elementData[--elementCount] = null; // Let gc do its work

    // 返回旧值
    return oldValue;
}

为什么说Vector是线程安全的?
可以看到Vector的操作都是使用synchronized关键字进行同步的,也就是说是线程安全的,由于Vector是同步的,所以效率要比ArrayList低

Vector的简单使用
创建一个Vector向量,添加元素

public class HashDemo {
    public static void main(String[] args) {
        Vector<String> vector = new Vector<>();
        vector.add("ArrayList");
        vector.add("HashMap");
        vector.add("ConcurrentHashMap");
        vector.add("LinkedList");
        vector.add("LinkedHashMap");
        System.out.println(vector);
    }
}

输出结果为:

[ArrayList, HashMap, ConcurrentHashMap, LinkedList, LinkedHashMap]

使用迭代器输出结果

public class HashDemo {
    public static void main(String[] args) {
        Vector<String> vector = new Vector<>();
        vector.add("ArrayList");
        vector.add("HashMap");
        vector.add("ConcurrentHashMap");
        vector.add("LinkedList");
        vector.add("LinkedHashMap");

        Iterator<String> iterator = vector.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next()); // 这里一定要加上next(),不然会出现死循环
        }
    }
}

输出结果为:

ArrayList
HashMap
ConcurrentHashMap
LinkedList
LinkedHashMap

本站文章版权归原作者及原出处所有 。内容为作者个人观点, 并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是一个个人学习交流的平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽,造成漏登,请及时联系我们,我们将根据著作权人的要求,立即更正或者删除有关内容。本站拥有对此声明的最终解释权。

回到顶部
嘿,我来帮您!