博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Vector源码解析
阅读量:5272 次
发布时间:2019-06-14

本文共 5616 字,大约阅读时间需要 18 分钟。

简介

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

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
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的最大值,将报错。

枚举器 Enumeration

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

public Enumeration
elements() { return new Enumeration
() { 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。但是如果在代码中像如下的方式使用,还是会出问题。

public void enumNoFailFastTest() {        Vector
vector = new Vector<>(); vector.add("chris"); vector.add("mumu"); Enumeration
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
c) { final int expectedModCount = modCount; Arrays.sort((E[]) elementData, 0, elementCount, c); if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } modCount++; }

疑问

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

转载于:https://www.cnblogs.com/arax/p/9163006.html

你可能感兴趣的文章
java选择文件时提供图像缩略图[转]
查看>>
方维分享系统二次开发, 给评论、主题、回复、活动 加审核的功能
查看>>
Matlab parfor-loop并行运算
查看>>
string与stringbuilder的区别
查看>>
2012-01-12 16:01 hibernate注解以及简单实例
查看>>
iOS8统一的系统提示控件——UIAlertController
查看>>
PAT甲级——1101 Quick Sort (快速排序)
查看>>
python创建进程的两种方式
查看>>
1.2 基础知识——关于猪皮(GP,Generic Practice)
查看>>
迭代器Iterator
查看>>
java易错题----静态方法的调用
查看>>
php建立MySQL数据表
查看>>
最简单的线程同步的例子
查看>>
旅途上看的电影和观后感
查看>>
qt实现类似QQ伸缩窗口--鼠标事件应用
查看>>
Ztree异步树加载
查看>>
关于IE和火狐,谷歌,Safari对Html标签Object和Embed的支持问题
查看>>
poj3320 Jessica's Reading Problem(尺取思路+STL)
查看>>
分布式计算开源框架Hadoop介绍
查看>>
安卓平台接口剖析
查看>>