原地徘徊一千步,也抵不上迈出一步。心中想无数次,也不如真正行动一次。
内容
1.Vector概述
Vector,来自于JDK1.0 的古老集合类,继承自 AbstractList,实现了 List 接口 ,底层是数组结构,元素可重复,有序(存放顺序),支持下标索引访问,允许null元素。
Vector所有方法的实现都是同步的(被synchronized修饰),数据安全,因此效率低,可以看成ArrayList的同步版本。Vector与ArrayList十分相似,所以建议先阅读:Java容器深度总结:ArrayList
Vector类图:
Vector定义:
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
Vector与ArrayList在定义上几乎一致:
- Vector< E > :Vector支持泛型。
- 继承 AbstractList< E >:继承AbstractList,此类提供 List 接口的骨干实现。
- 实现 List< E >:实现了List接口,该接口包含了有关列表的基本操作。
- 实现RandomAccess, Cloneable, java.io.Serializable接口:表明Vector具备快速随机访问能力,可以调用clone()克隆,实现序列化。
2.Vector主要成员变量
- protected Object[] elementData : 存放元素的底层容器,为Object类型的数组,数组长度为Vector的容量。
- protected int elementCount:实际保存元素的个数。
- protected int capacityIncrement:容量增量,用于扩容时计算新容量大小。
3.构造函数和初始容量
Vector的构造方法有以下四个:
- Vector()
- Vector(int initialCapacity)
- Vector(int initialCapacity, int capacityIncrement)
- Vector(Collection<? extends E> c)
3.1 Vector()
Vector无参构造调用了Vector(int initialCapacity)构造函数,初始化数据数组(elementData)的容量为10,容量增量(capacityIncrement)为0。
public Vector() {
this(10); // 调用Vector(int initialCapacity)构造函数,初始容量10
}
3.2 Vector(int initialCapacity)
Vector(int initialCapacity)调用Vector(int initialCapacity, int capacityIncrement)构造函数,自定义数组容量,容量增量(capacityIncrement)为0。
public Vector(int initialCapacity) {
this(initialCapacity, 0);
}
3.3 Vector(int initialCapacity, int capacityIncrement)
创建指定容量的数组,指定容量增量(capacityIncrement)的值。
public Vector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
- initialCapacity 初始化容量小于0时抛出异常。
- 创建 initialCapacity 容量的数组。
- 赋值容量增量(capacityIncrement )。
3.4 Vector(Collection<? extends E> c)
构建一个包含指定 Collection 中的元素的集合,数组容量和元素个数为Collection 集合的元素个数,容量增量为0(未指定容量增量,默认值为0),并且这些元素按其 collection 的迭代器返回元素的顺序排列。
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);
}
- 将Collection集合转为数组赋值给elementData。
- 赋值元素个数(elementCount )为elementData的长度。
- 若elementData类型不是Object,则转化成Object数组。
4.Vector扩容机制
Vector扩容机制与ArrayList相似,比ArrayList的扩容机制简单一些。Vector添加元素时,由ensureCapacityHelper()方法保证容量足够。
Vector扩容的执行流程:
- 判断是否需要扩容;
- 计算新容量;
- 考虑数组长度溢出;
- 扩容
4.1 判断是否需要扩容
当所需要的最小容量(minCapacity)大于数组的容量时,则调用grow()方法进行扩容。
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
Tip:最小容量(minCapacity)为添加元素后的总元素个数。
4.2 计算新容量
确定要进行扩容后,先计算扩容后数组的新容量。
private void grow(int minCapacity) {
// 计算新容量
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 考虑数组长度溢出
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//扩容
elementData = Arrays.copyOf(elementData, newCapacity);
}
注意:
新容量的计算方法:newCapacity = oldCapacity + ((capacityIncrement > 0) ? capacityIncrement : oldCapacity);
- 当未指定容量增量(capacityIncrement )或 capacityIncrement <= 0 时,新容量(newCapacity )为 旧容量(oldCapacity )的2倍。
- 否则(容量增量 > 0 时),新容量(newCapacity )为 旧容量和容量增量的和。
- 若计算出来的新容量(newCapacity )小于最小容量(minCapacity),则新容量取最小容量。
Tip: ArrayList扩容时,新容量约为旧容量的 1.5 倍。
4.3 考虑数组长度溢出
不管是计算出的新容量(newCapacity ),还是最小容量(minCapacity)都存在数值溢出的情况,Vector考虑数组长度溢出和ArrayList相同。
// 考虑数组长度溢出
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// MAX_ARRAY_SIZE : 建议最大数组容量
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;
}
- 当minCapacity溢出时抛出OutOfMemoryError异常;
- minCapacity > MAX_ARRAY_SIZE 时 设置 newCapacity 等于Integer.MAX_VALUE。
- minCapacity <= MAX_ARRAY_SIZE 时 设置 newCapacity 等于 MAX_ARRAY_SIZE。
4.4 扩容
通过Arrays.copyOf()重新构建一个数组,然后将原来数组的元素拷贝到新数组中,新数组的长度为计算出的新容量(newCapacity)。并修改原数组的引用指向这个新建数组,原数组自动抛弃(Java垃圾回收机制会自动回收)。
//扩容
elementData = Arrays.copyOf(elementData, newCapacity);
4.5 Vector扩容总结
Vector添加元素时使用 ensureCapacityHelper() 方法来保证容量足够,如果不够时,需要使用 grow() 方法进行扩容,当未指定容量增量时,默认新容量为旧容量的2倍,指定容量增量时,新容量为旧容量与容量增量的和。
扩容操作需要调用 Arrays.copyOf() 把原数组整个复制到新数组中,这个操作代价很高,因此应该尽量减少扩容次数。
- 创建Vector对象时,可以指定大概容量。
- add之前调用ensureCapacity()方法指定大概容量。
- 根据实际情况,选择是否指定容量增量和容量增量大小。
5.常用方法
Vector所有的方法的实现都是同步的。
签名 | 描述 | 时间复杂度 |
---|---|---|
E elementAt(int index) | 获取指定位置的元素 | O(1) |
void addElement(E obj) | 在集合末尾新增一个元素 | O(1) |
void add(int index, E element) | 在指定位置添加元素 | O(n) |
boolean addAll(int index, Collection<? extends E> c) | 在指定位置添加集合中的元素 | O(n) |
void removeElementAt(int index) | 删除指定位置的元素 | O(n) |
boolean removeElement(Object obj) | 移除集合中第一次出现的指定元素 | O(n) |
void removeAllElements() | 从列表中移除所有元素 | O(n) |
void insertElementAt(E obj, int index) | 在指定索引位置中插入元素 | O(n) |
E set(int index, E element) | 设置指定位置的元素值 | O(1) |
boolean contains(Object o) | 是否包含某个元素 | O(n) |
int size() | 返回列表中的元素个数 | O(1) |
boolean isEmpty() | 列表是否为空 | O(1) |
6.Enumeration迭代器
由于Vector属于List接口集合体系,因此具有通用的iterator 和 listIterator迭代器,同时Vector还具有Enumeration迭代器。
Enumeration迭代器是同Vector在JDK1.0就存在了,因此比较古老,功能单一:
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");
}
};
}
Enumeration是一个接口,Vector中的elements()方法返回了Enumeration接口的匿名实现类。
Enumeration迭代器特点:
- 只能正向迭代。
- 只能用于迭代,没有其他额外的功能。
- 没有快速失败机制(fail-fast)。
由于Enumeration迭代器没有iterator和listIterator迭代器的快速失败机制,因此不会抛出并发修改的异常(ConcurrentModificationException),因此容易产生一些比较严重的问题:
public class VectorTest {
public static void main(String[] args) {
Vector vector = new Vector(Arrays.asList(1,2,3,4));
//获取自己的迭代器Enumeration
Enumeration elements = vector.elements();
while (elements.hasMoreElements()) {
vector.add(1);
System.out.println(elements.nextElement());
} // 死循环
}
}
用于Enumeration迭代器通过内部的count变量和Vector的元素个数elementCount比较来判断是否能继续迭代,因此在每次迭代时添加元素,将会导致elementCount永远比count大,从而造成死循环,直到OutOfMemory。
注意:Iterator比Enumeration覆盖范围广,功能更强大。因此迭代时尽量使用Iterator和ListIterator。
7.快速失败(fail-fast)
Vector也是快速失败(fail-fast)的,其实现原理和ArrayList中的相同,此处不再赘述。
8.序列化
同ArrayList一样,Vector也实现了writeObject()和readObject()方法,区别是Vector的内部数组(elementData)被全部序列化了,而ArrayList只序列化了elementData中保存元素的那部分。
Vector:
private void writeObject(java.io.ObjectOutputStream s)
throws java.io.IOException {
final java.io.ObjectOutputStream.PutField fields = s.putFields();
final Object[] data;
synchronized (this) {
fields.put("capacityIncrement", capacityIncrement);
fields.put("elementCount", elementCount);
data = elementData.clone();
}
fields.put("elementData", data);
s.writeFields();
}
private void readObject(ObjectInputStream in)
throws IOException, ClassNotFoundException {
ObjectInputStream.GetField gfields = in.readFields();
int count = gfields.get("elementCount", 0);
Object[] data = (Object[])gfields.get("elementData", null);
if (count < 0 || data == null || count > data.length) {
throw new StreamCorruptedException("Inconsistent vector internals");
}
elementCount = count;
elementData = data.clone();
}
9.clone机制
Vector的clone()方法返回的是一个全新的Vector实例对象,但是其elementData,也就是存储数据的数组,存储的对象还是指向了旧的Vector存储的那些对象。也就是Vector这个类实现了深拷贝,但是对于存储的对象还是浅拷贝。
public synchronized Object clone() {
try {
@SuppressWarnings("unchecked")
Vector<E> v = (Vector<E>) super.clone();
v.elementData = Arrays.copyOf(elementData, elementCount);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
10.与ArrayList比较
Vector与ArrayList底层是数组结构,元素可重复,有序(存储顺序),支持下标索引访问,允许null元素。
- Vector 是同步的,因此效率低;ArrayList 线程不安全,效率高。
- Vector 每次扩容请求其大小的 2 倍(也可以通过构造函数设置增长的容量); ArrayList 是 1.5 倍。
- Vector序列话整个elementData数组;ArrayList只序列化elementData数组中保存元素的那部分。