Vector
一:Vector概述
vector实现了一个可以自动增长的对象数组,类似于动态数组,能够动态的调整自身的大小,能够根据索引进行查询
vector继承了AbstractList, 实现了List接口,是一个队列,实现了相应的CRUD功能
vector实现了RandomAccess接口,可以实现对数据进行随机的访问
vector的操作是线程安全的,vector中的大部分的方法都增加了synchronized关键字修饰
二:源码分析
1:成员变量
public class Vector<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
// elementData是一个数组类型,初始化的大小是10,会随着元素的增多不断的增长。
protected Object[] elementData;
// elementCount是数组中元素的个数。
protected int elementCount;
// capacityIncrement是数组在扩容时增长的系数,如果不指定这个系数的大小时,默认为0
// 如果capacityIncrement<=0时,那么每次扩容elementData的时候就是直接翻倍(n -> 2 * n)
protected int capacityIncrement;
// 序列化id
private static final long serialVersionUID = -2767605614048989439L;
}
2:构造方法
vector中构造方法有4个
//不指定任何参数时,默认数组的大小为10
public Vector() {
this(10);
}
//仅指定数组的大小 - 容量输入的多少就是多少
public Vector(int initialCapacity) {
// 0是声明扩容系数为0
this(initialCapacity, 0);
}
//指定数组的大小和扩容系数
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0) {
throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
}
// 构建数组,同时赋值扩容系数
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
//初始化指定的集合
public Vector(Collection<? extends E> c) {
elementData = c.toArray();
elementCount = elementData.length;
if (elementData.getClass() != Object[].class) {
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
}
3:增加和减少数组大小
在Vector中由于数据量是不固定大小的,当数据量超过当前的容量时,需要对数组的容量进行扩容。
每次扩容的大小是由三个参数来决定的:
- 扩容的目标大小minCapacity
- 扩容系数capacityIncrement
- 当前数组的容量大小
如果目标大小小于当前数组的容量,就不需要再进行扩容操作(target < cap --> nothing todo)
否则先将数组的容量按照capacityIncrement进行扩容,若扩容后的大小仍小于目标大小,那么就将容量大小改为目标大小。
每次对Vector进行结构改变的操作,例如insert、add等都会执行ensyreCapacityHelper方法对Vector的容量进行检查
🎉 而在进行扩容操作时存在数组的复制等操作,因此如果存在插入大量元素的操作时,可以提前执行一次扩容操作,减少执行ensureCapacityHelper的操作,可以提高Vector中某些方法的执行效率。
在每次执行增加元素的操作时,方法会自动调用ensureCapacityHelper的操作,使用者可以不用去担心容量是否够用,但是如果Vector的容量远大于其中存储的元素的个数时,也会存在空间的浪费,那么就可以调用trimSize方法,对Vector的中未使用到的空间进行释放。
// 数组缩容
// 减少数组占用的空间:当数组中实际存储的元素个数小于数组的容量时,可以减少数组的容量为实际存储元素大小的容量
// 存在元素的复制
public synchronized void trimToSize() {
modCount++; // 修改次数属性 + 1
int oldCapacity = elementData.length; // 拿到原始数组的长度
if (elementCount < oldCapacity) { // 缩容到指定的大小
elementData = Arrays.copyOf(elementData, elementCount);
}
}
// 扩展数组的容量大小为指定的minCapacity大小
public synchronized void ensureCapacity(int minCapacity) {
modCount++;
ensureCapacityHelper(minCapacity);
}
//对数组进行扩容,这个方法不是同步的,该类中的同步方法可以在内部调用这个
// 确保生产能力但是不花费额外的成本去进行同步--存在元素的复制
// 如果数组的容量小于指定的容量,先按照capacityIncrement系数进行扩容
// 如果还是小于指定的容量,则扩容到指定的容量
// 如果数组中要一次性增加大量元素时,可以先调用这个方法对数组进行扩容,避免在每次add时去扩容,减少扩容的次数
private void ensureCapacityHelper(int minCapacity) {
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
Object[] oldData = elementData;
int newCapacity = (capacityIncrement > 0) ?(oldCapacity + capacityIncrement) : (oldCapacity * 2);
if (newCapacity < minCapacity) {
newCapacity = minCapacity;
}
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
4:vector的遍历
public static void VectorTest1() {
Vector vector = new Vector();
long s1 = System.currentTimeMillis();
for(int i = 0; i< 10000000; i++) {
vector.add(i);
}
//System.out.println("初始化时间:"+(System.currentTimeMillis()-s1));
s1 = System.currentTimeMillis();
//通过Iterator进行遍历
Object obj = null;
Iterator<Object> iter = vector.iterator();
while(iter.hasNext()) {
obj = iter.next();
}
System.out.println("iterator花费时间:"+(System.currentTimeMillis()-s1));
s1 = System.currentTimeMillis();
//随机访问,通过索引进行遍历
Object obj1 = null;
int size = vector.size();
for(int i = 0; i< size; i++) {
obj1 = vector.get(i);
}
System.out.println("索引遍历花费时间:"+(System.currentTimeMillis()-s1));
s1 = System.currentTimeMillis();
Object obj2 = null;
for(Object o : vector) {
obj2 = o;
}
System.out.println("for循环花费时间:"+(System.currentTimeMillis()-s1));
s1 = System.currentTimeMillis();
//通过Enumeration遍历
Object obj3 = null;
Enumeration enumer = vector.elements();
while(enumer.hasMoreElements()) {
obj3 = enumer.nextElement();
}
System.out.println("Enumeration花费时间:"+(System.currentTimeMillis()-s1));
}
三:其他主要方法一览
方法 | 说明 |
---|---|
void setSize(int newSize) | 修改vector中包含的元素的个数 |
int capacity() | 返回当前vector的容量大小 |
int size() | 返回当前vector包含的元素个数 |
boolean isEmpty() | 判断vector是否为空 |
boolean contains(Object o) | 判断Vector中是否包含元素o |
int indexOf(Object o) | 返回Vector中元素o的索引位置 |
int indexOf(Object o, int index) | 从index位置开始查询,返回元素o的位置 |
int lastIndexOf(Object o, int index) | 从index位置向前查询,最后一次 |
E elementAt(int index) E get(int index) | 返回index位置的元素值 |
E firstElement() | 返回第一个元素值 |
E lastElement() | 返回最后一个元素值 |
void setElementAt(E obj, int index) | 修改index位置的元素值为obj |
void removeElementAt(int index) | 删除index位置的元素值 |
void insetElementAt(E obj, int index) void add(int index, E element) | 插入一个元素在index位置 |
void addElement(E obj) boolean add(E e) | 在数组的最后一个位置插入元素 |
boolean removeElement(Object obj) boolean remove(Object obj) | 删除一个元素 |
Object[] toArray() | 将vector转化为数组 |
T[] toArray(T[] a) | 将vector转化为指定的数组 |
E set(int index, E element) | 修改index位置的元素值,返回替换前的值 |
E remove(int index) | 删除index位置的值,返回删除前的值 |
boolean containsAll(Collection<?> c) | 判断vector中是否包含指定集合中的所有元素 |
boolean addAll(Collection<?> c) | 将C中的所有元素加到vector中 |
boolean retainAll(Collection<?> c) | 判断vector中是否有元素不再C中 |
boolean addAll(int index, Collection<?> c) | 在指定位置插入c中的所有元素 |
boolean equals(Object o) | 判断两个集合是否相等 |
List subList(int fi, int ti) | 截取vector,返回的类型为List,返回值时索引值为fi-ti之间的值 |
void removeRange(int fi, int ti) | 删除vector中索引值为fi到ti之间的元素 |
四:vector的使用
Vector为存储的对象分配的是一块连续的存储空间,他的本质上是一个动态数组,可以通过索引进行访问,因此随机访问的效率很高,但是在Vector中插入或删除一个元素时,会对数组中的元素进行复制和移动操作
当Vector中存储的数据量很大的情况下,对元素的进行复制操作时开销比较大。
而List中的元素是以链表的形式存储的,当访问一个元素的时候都需要对链表进行遍历,但是相比较Vector,List的插入和删除元素操作比较方便。
因此Vector比较适用于:对象的数量变化较少,简单对象,随机访问较频繁的情况中,而List比较适用于对象数量变化大,对象比较负责,频繁插入和删除操作的情况。