Vector源码解析

简介

Vector 看上去想一个可增加的数组,可使用索引访问。他的size随着添加或删除item可大可小。Vector为了优化存储,保存着capacity和capacityIncrement两个变量。capacity最小为vector当前的大小。通常比vector的size要大,当有有数据添加到vector时,vector的大小以capacityIncrement的整数倍增长。java

vector的迭代器也是fail-fast的,若是在迭代器建立后vector的结构被修改了(除了调用迭代器自己的remove或者add方法),将会抛出异常。数组

since v1.2 vector 实现了List接口,并且开始支持多线程。支持多线程的方式是在方法前加上synchronized修饰符,实现对象锁,这种锁的效率很低。安全

构造函数

Vector的构造函数有以下四个:多线程

// 能够指定初始容量,和每次扩容的数量
    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 其内部在扩容时
    // 判断了若是capacityIncrement<= 0 时,直接扩容到原来
    // 容量的两倍,在grow函数中。
    public Vector(int initialCapacity) {
        this(initialCapacity, 0);
    }
    // 默认构造函数,初始容量为10,jdk1.8中ArrayList默认构造函数的初始容量为0在第一调用add时将其初始化为10
    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);
    }

扩容机制

vector内部使用数组存储:函数

protected Object[] elementData;

当数组大小不够用时,它会使用内部的扩容机制扩容。扩容机制写的很严谨,考虑到了int超过最大值的状况。优化

// 在添加元素会调用传进来的minCapacity 为当前vector内部的数据个数,加上要增长的数据个数 minCapacity 由于使用的int,在扩容时有可能超过int最大值而变成一个负数
    public synchronized void ensureCapacity(int minCapacity) {
        if (minCapacity > 0) {
            modCount++;
            ensureCapacityHelper(minCapacity);
        }
    }
    private void ensureCapacityHelper(int minCapacity) {
        // overflow-conscious code
        // 若是须要的容量大于当前数组的容量才扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }   
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        // 若是构造函数中没有设置capacityIncrement,则扩容到以前容量的两倍
        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                         capacityIncrement : oldCapacity);
        // 若是扩容到原来的两倍还不够用,则使用指定的容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
            // 一样考虑超过容许的最大值状况,将调用hugeCapactity
            // MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
            // 虚拟机为数组保留了一些头信息
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
    // 若是超过Integer.MAX_VALUE-8 则返回int最大值,不然返回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;
    }

总结一下,在扩容时,若是定义了capacityIncrement,按照capacityIncrement增长,若是capacityIncrement为0,将容量扩大到原来的两倍,扩容后若是还不够用的话,按照实际大小扩容。其中若是扩容后的大小超过了int的最大值,将报错。this

枚举器 Enumeration

Vector比ArrayList多提供一个枚举器,相似迭代器。线程

public Enumeration<E> elements() {
        return new Enumeration<E>() {
            int count = 0;

            public boolean hasMoreElements() {
                return count < elementCount;
            }

            public E nextElement() {
                synchronized (Vector.this) {
                    if (count < elementCount) {
                        return elementData(count++);
                    }
                }
                throw new NoSuchElementException("Vector Enumeration");
            }
        };
    }

从代码中能够看出其结构很相似interator的内部实现,调用hasMoreElement判断是否还有元素,调用nextElement迭代出下一个元素。可是其内部没有像迭代器中的remove方法,很纯粹,是一个只读的模型。他与迭代器最大的区别是,当枚举建立后,若是对vector的结构作了修改,他不会抛出ConcurrentModificationException。可是若是在代码中像以下的方式使用,仍是会出问题。code

public void enumNoFailFastTest() {
        Vector<String> vector = new Vector<>();
        vector.add("chris");
        vector.add("mumu");
        Enumeration<String> enumeration = vector.elements();

        System.out.println(enumeration.nextElement());
        if (enumeration.hasMoreElements()) {
            vector.remove(1);
            System.out.println(enumeration.nextElement());
        }
    }

上述代码运行后抛出异常:java.util.NoSuchElementException: Vector Enumeration。缘由是当用hashasMoreElements后,将第二个元素移除了,这时再次调用nextElement时,发现count=1 == elementCount了。这时就会抛出异常。这里是模拟在一个线程在判断还有元素(最后一个元素)后,第二个线程移除了这个元素,第一个线程再访问这个元素就会出错。基于上面的分析,若是在写代码时用到了elements这个函数,须要本身处理线程安全的问题。对象

其余知识点

除此以外,vector还提供了一些相似数组按照索引访问的功能。例如,indexOf,lastIndexOf,firstElement,lastElement,setElement,removeElement,elementAt等函数,这里再也不讲其内部实现。其内部使用了不少一下两个关于数组的函数,对日常coding颇有帮助。

//数组的copy函数
    // src : 从哪一个数组copy数据
    // srcPos : 从数组的那个索引开始copy
    // dest : copy 到哪一个函数中,
    // destPos : copy的数据从dest的第几个索引开始存储
    // length: copy数据的长度
    System.arraycopy(Object src,  int  srcPos, Object dest, int destPos,int length);
    // 以给定的数据类型,指定的数组长度建立一个数组
    Array.newInstance(newType.getComponentType(), newLength);

Vector支持排序,在排序的过程当中,vector若是发生结构变化,也会抛出异常。而排序内部使用Arrays提供的排序方法。

public synchronized void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, elementCount, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

疑问

在vector的源码注解中有一个shrink,意思当删除数据时,他会自动缩小。可是在代码中没有看到关于缩小elementData的操做,只有一个trimTosize()函数。若是非要说他能够缩小所占空间的话,应该指的是这个函数。

相关文章
相关标签/搜索