深入剖析 Java Vector:从源码洞悉其使用原理
一、引言
在 Java 的集合框架中,数据结构犹如构建程序大厦的基石,为开发者提供了丰富多样的选择来存储和管理数据。其中,Vector
作为早期 Java 就引入的一个集合类,曾经在众多项目中扮演着重要角色。尽管在现代 Java 开发中,由于其线程安全机制带来的性能开销,Vector
的使用频率逐渐被其他集合类(如 ArrayList
)所取代,但了解 Vector
的使用原理和内部实现机制,不仅有助于我们深入理解 Java 集合框架的设计思想,还能让我们在特定的场景下做出更合适的选择。
本文将深入到 Vector
的源码层面,详细剖析其使用原理,包括基本结构、常用方法的实现细节、线程安全机制以及性能特点等方面,帮助读者全面掌握 Vector
这个数据结构。
二、Vector 概述
2.1 基本概念
Vector
是 Java 集合框架中的一个动态数组实现,它实现了 List
接口,这意味着它可以像普通列表一样存储和操作一组有序的元素。与 ArrayList
类似,Vector
可以自动扩容以适应不断增加的元素,但不同的是,Vector
是线程安全的,这使得它在多线程环境下可以安全地使用,而无需额外的同步机制。
2.2 继承关系与接口实现
下面是 Vector
类的定义以及它的继承关系和接口实现:
// Vector 类继承自 AbstractList 类,并实现了 List、RandomAccess、Cloneable 和 Serializable 接口
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 内部代码省略
}
AbstractList
:提供了List
接口的部分实现,减少了Vector
类需要实现的方法数量,提高了代码的复用性。List
:表示Vector
是一个列表,可以存储一组有序的元素,并且支持列表的各种操作,如添加、删除、获取元素等。RandomAccess
:这是一个标记接口,表明Vector
支持随机访问,即可以通过索引快速访问列表中的元素,时间复杂度为 O ( 1 ) O(1) O(1)。Cloneable
:表示Vector
支持克隆操作,允许创建一个与原对象内容相同的新对象。Serializable
:表示Vector
可以进行序列化,方便在网络传输或持久化存储时将对象转换为字节流。
2.3 与其他集合类的对比
在 Java 集合框架中,与 Vector
功能相似的集合类主要有 ArrayList
和 LinkedList
。下面简单对比一下它们的特点:
ArrayList
:也是一个动态数组实现,与Vector
不同的是,ArrayList
不是线程安全的。在单线程环境下,由于不需要额外的同步开销,ArrayList
的性能通常比Vector
更好。LinkedList
:采用链表结构实现,与Vector
和ArrayList
的数组结构不同。LinkedList
在插入和删除操作上具有优势,特别是在列表的头部和尾部进行操作时,但随机访问性能较差。
三、Vector 的内部结构
3.1 核心属性
Vector
类内部定义了几个核心属性,用于存储元素和管理数组的状态。以下是这些属性的源码:
// 存储元素的数组
protected Object[] elementData;
// 数组中实际存储的元素数量
protected int elementCount;
// 数组的容量增量,当数组需要扩容时,容量会增加这个值
protected int capacityIncrement;
elementData
:这是一个Object
类型的数组,用于实际存储Vector
中的元素。由于 Java 数组的长度是固定的,当元素数量超过数组的容量时,需要对数组进行扩容。elementCount
:表示数组中实际存储的元素数量,它的值始终小于或等于elementData
数组的长度。capacityIncrement
:数组的容量增量,当数组需要扩容时,容量会增加这个值。如果capacityIncrement
为 0,则每次扩容时数组的容量会翻倍。
3.2 构造函数
Vector
提供了多个构造函数,允许开发者根据不同的需求创建 Vector
对象。以下是这些构造函数的源码:
// 构造一个空的 Vector,初始容量为 10,容量增量为 0
public Vector() {
// 调用另一个构造函数,指定初始容量为 10
this(10);
}
// 构造一个空的 Vector,指定初始容量,容量增量为 0
public Vector(int initialCapacity) {
// 调用另一个构造函数,指定初始容量和容量增量
this(initialCapacity, 0);
}
// 构造一个空的 Vector,指定初始容量和容量增量
public Vector(int initialCapacity, int capacityIncrement) {
// 检查初始容量是否为负数
super();
if (initialCapacity < 0)
// 如果初始容量为负数,抛出 IllegalArgumentException 异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
// 创建一个指定初始容量的数组
this.elementData = new Object[initialCapacity];
// 初始化容量增量
this.capacityIncrement = capacityIncrement;
}
// 构造一个包含指定集合元素的 Vector,元素顺序与集合的迭代器返回顺序相同
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)
// 如果数组类型不是 Object[],则将其复制到一个新的 Object[] 数组中
elementData = Arrays.copyOf(elementData, elementCount, Object[].class);
}
- 无参构造函数:创建一个初始容量为 10,容量增量为 0 的
Vector
对象。 - 带初始容量参数的构造函数:创建一个指定初始容量,容量增量为 0 的
Vector
对象。 - 带初始容量和容量增量参数的构造函数:创建一个指定初始容量和容量增量的
Vector
对象。 - 带集合参数的构造函数:创建一个包含指定集合元素的
Vector
对象,元素顺序与集合的迭代器返回顺序相同。
3.3 数组扩容机制
当 Vector
中的元素数量超过数组的容量时,需要对数组进行扩容。Vector
的扩容机制主要通过 ensureCapacityHelper
方法实现,以下是该方法的源码:
// 确保数组的容量足够容纳指定的最小容量
private void ensureCapacityHelper(int minCapacity) {
// 如果最小容量超过数组的当前容量
if (minCapacity - elementData.length > 0)
// 调用 grow 方法进行扩容
grow(minCapacity);
}
// 数组的最大容量
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
// 扩容数组
private void grow(int minCapacity) {
// 获取旧数组的容量
int oldCapacity = elementData.length;
// 计算新数组的容量
int newCapacity = oldCapacity + ((capacityIncrement > 0)?
capacityIncrement : oldCapacity);
// 如果新容量小于最小容量,则将新容量设置为最小容量
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
// 如果新容量超过最大数组容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 调用 hugeCapacity 方法处理超大容量的情况
newCapacity = hugeCapacity(minCapacity);
// 将旧数组复制到新数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
// 处理超大容量的情况
private static int hugeCapacity(int minCapacity) {
// 如果最小容量小于 0,抛出 OutOfMemoryError 异常
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
// 如果最小容量超过最大数组容量,则返回 Integer.MAX_VALUE,否则返回最大数组容量
return (minCapacity > MAX_ARRAY_SIZE)?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
ensureCapacityHelper
方法:检查数组的当前容量是否足够容纳指定的最小容量,如果不够,则调用grow
方法进行扩容。grow
方法:计算新数组的容量,通常是旧容量加上容量增量(如果容量增量大于 0),或者是旧容量的两倍(如果容量增量为 0)。如果新容量小于最小容量,则将新容量设置为最小容量。如果新容量超过最大数组容量,则调用hugeCapacity
方法处理超大容量的情况。最后,使用Arrays.copyOf
方法将旧数组复制到新数组中。hugeCapacity
方法:处理超大容量的情况,如果最小容量小于 0,抛出OutOfMemoryError
异常。如果最小容量超过最大数组容量,则返回Integer.MAX_VALUE
,否则返回最大数组容量。
四、基本操作的源码分析
4.1 添加元素
4.1.1 add(E e) 方法
add(E e)
方法用于在 Vector
的末尾添加一个元素。以下是该方法的源码:
// 在 Vector 的末尾添加一个元素
public synchronized boolean add(E e) {
// 确保数组的容量足够容纳新元素
modCount++;
ensureCapacityHelper(elementCount + 1);
// 将新元素添加到数组的末尾
elementData[elementCount++] = e;
return true;
}
synchronized
关键字:表明该方法是线程安全的,同一时间只能有一个线程访问该方法,避免多个线程同时修改Vector
导致数据不一致的问题。modCount
:这是AbstractList
类中的一个属性,用于记录Vector
结构修改的次数,主要用于迭代器的并发修改检查。ensureCapacityHelper
方法:确保数组的容量足够容纳新元素,如果不够,则进行扩容。elementData[elementCount++] = e
:将新元素添加到数组的末尾,并将elementCount
加 1。
4.1.2 add(int index, E element) 方法
add(int index, E element)
方法用于在指定位置插入一个元素。以下是该方法的源码:
// 在指定位置插入一个元素
public void add(int index, E element) {
// 调用 insertElementAt 方法进行插入操作
insertElementAt(element, index);
}
// 在指定位置插入一个元素
public synchronized void insertElementAt(E obj, int index) {
// 检查索引是否越界
modCount++;
if (index > elementCount) {
// 如果索引大于元素数量,抛出 ArrayIndexOutOfBoundsException 异常
throw new ArrayIndexOutOfBoundsException(index
+ " > " + elementCount);
}
// 确保数组的容量足够容纳新元素
ensureCapacityHelper(elementCount + 1);
// 将指定位置及其后面的元素向后移动一位
System.arraycopy(elementData, index, elementData, index + 1, elementCount - index);
// 将新元素插入到指定位置
elementData[index] = obj;
// 元素数量加 1
elementCount++;
}
insertElementAt
方法:首先检查索引是否越界,如果越界则抛出ArrayIndexOutOfBoundsException
异常。然后调用ensureCapacityHelper
方法确保数组的容量足够容纳新元素。接着使用System.arraycopy
方法将指定位置及其后面的元素向后移动一位,最后将新元素插入到指定位置,并将elementCount
加 1。
4.1.3 addAll(Collection<? extends E> c) 方法
addAll(Collection<? extends E> c)
方法用于将指定集合中的所有元素添加到 Vector
的末尾。以下是该方法的源码:
// 将指定集合中的所有元素添加到 Vector 的末尾
public synchronized boolean addAll(Collection<? extends E> c) {
// 记录结构修改次数
modCount++;
// 将集合转换为数组
Object[] a = c.toArray();
// 获取数组的长度
int numNew = a.length;
// 确保数组的容量足够容纳新元素
ensureCapacityHelper(elementCount + numNew);
// 将数组中的元素复制到 Vector 的末尾
System.arraycopy(a, 0, elementData, elementCount, numNew);
// 更新元素数量
elementCount += numNew;
// 如果添加的元素数量大于 0,则返回 true,否则返回 false
return numNew != 0;
}
- 该方法首先记录结构修改次数,然后将集合转换为数组,调用
ensureCapacityHelper
方法确保数组的容量足够容纳新元素。接着使用System.arraycopy
方法将数组中的元素复制到Vector
的末尾,并更新elementCount
。最后根据添加的元素数量是否大于 0 返回相应的结果。
4.2 删除元素
4.2.1 remove(int index) 方法
remove(int index)
方法用于删除指定位置的元素。以下是该方法的源码:
// 删除指定位置的元素
public synchronized E remove(int index) {
// 记录结构修改次数
modCount++;
// 检查索引是否越界
if (index >= elementCount)
// 如果索引越界,抛出 ArrayIndexOutOfBoundsException 异常
throw new ArrayIndexOutOfBoundsException(index);
// 获取要删除的元素
E oldValue = elementData(index);
// 计算需要移动的元素数量
int numMoved = elementCount - index - 1;
if (numMoved > 0)
// 将指定位置后面的元素向前移动一位
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 将数组的最后一个元素置为 null,帮助垃圾回收
elementData[--elementCount] = null; // Let gc do its work
// 返回被删除的元素
return oldValue;
}
// 获取指定位置的元素
E elementData(int index) {
return (E) elementData[index];
}
- 该方法首先记录结构修改次数,然后检查索引是否越界。接着获取要删除的元素,计算需要移动的元素数量,并使用
System.arraycopy
方法将指定位置后面的元素向前移动一位。最后将数组的最后一个元素置为null
,帮助垃圾回收,并返回被删除的元素。
4.2.2 remove(Object o) 方法
remove(Object o)
方法用于删除 Vector
中第一个匹配的元素。以下是该方法的源码:
// 删除 Vector 中第一个匹配的元素
public boolean remove(Object o) {
// 调用 removeElement 方法进行删除操作
return removeElement(o);
}
// 删除 Vector 中第一个匹配的元素
public synchronized boolean removeElement(Object obj) {
// 记录结构修改次数
modCount++;
// 查找元素的索引
int i = indexOf(obj);
if (i >= 0) {
// 如果找到元素,则调用 removeElementAt 方法删除该元素
removeElementAt(i);
return true;
}
return false;
}
// 返回元素在 Vector 中第一次出现的索引,如果不存在则返回 -1
public int indexOf(Object o) {
return indexOf(o, 0);
}
// 从指定位置开始查找元素在 Vector 中第一次出现的索引,如果不存在则返回 -1
public synchronized int indexOf(Object o, int index) {
if (o == null) {
// 如果要查找的元素为 null
for (int i = index ; i < elementCount ; i++)
if (elementData[i]==null)
return i;
} else {
// 如果要查找的元素不为 null
for (int i = index ; i < elementCount ; i++)
if (o.equals(elementData[i]))
return i;
}
return -1;
}
// 删除指定位置的元素
public synchronized void removeElementAt(int index) {
// 记录结构修改次数
modCount++;
// 检查索引是否越界
if (index >= elementCount) {
// 如果索引越界,抛出 ArrayIndexOutOfBoundsException 异常
throw new ArrayIndexOutOfBoundsException(index + " >= " +
elementCount);
}
else if (index < 0) {
// 如果索引为负数,抛出 ArrayIndexOutOfBoundsException 异常
throw new ArrayIndexOutOfBoundsException(index);
}
// 计算需要移动的元素数量
int j = elementCount - index - 1;
if (j > 0) {
// 将指定位置后面的元素向前移动一位
System.arraycopy(elementData, index + 1, elementData, index, j);
}
// 元素数量减 1
elementCount--;
// 将数组的最后一个元素置为 null,帮助垃圾回收
elementData[elementCount] = null; /* to let gc do its work */
}
remove(Object o)
方法调用removeElement
方法进行删除操作。removeElement
方法首先调用indexOf
方法查找元素的索引,如果找到元素,则调用removeElementAt
方法删除该元素。indexOf
方法用于查找元素在Vector
中第一次出现的索引,支持从指定位置开始查找。removeElementAt
方法用于删除指定位置的元素,与remove(int index)
方法的实现类似。
4.2.3 clear() 方法
clear()
方法用于清空 Vector
中的所有元素。以下是该方法的源码:
// 清空 Vector 中的所有元素
public synchronized void clear() {
// 记录结构修改次数
modCount++;
// 将数组中的所有元素置为 null,帮助垃圾回收
for (int i = 0; i < elementCount; i++)
elementData[i] = null;
// 元素数量置为 0
elementCount = 0;
}
- 该方法首先记录结构修改次数,然后将数组中的所有元素置为
null
,帮助垃圾回收。最后将elementCount
置为 0。
4.3 获取元素
4.3.1 get(int index) 方法
get(int index)
方法用于获取指定位置的元素。以下是该方法的源码:
// 获取指定位置的元素
public synchronized E get(int index) {
// 检查索引是否越界
if (index >= elementCount)
// 如果索引越界,抛出 ArrayIndexOutOfBoundsException 异常
throw new ArrayIndexOutOfBoundsException(index);
// 返回指定位置的元素
return elementData(index);
}
// 获取指定位置的元素
E elementData(int index) {
return (E) elementData[index];
}
- 该方法首先检查索引是否越界,如果越界则抛出
ArrayIndexOutOfBoundsException
异常。然后返回指定位置的元素。
4.3.2 firstElement() 方法
firstElement()
方法用于获取 Vector
中的第一个元素。以下是该方法的源码:
// 获取 Vector 中的第一个元素
public synchronized E firstElement() {
// 检查元素数量是否为 0
if (elementCount == 0) {
// 如果元素数量为 0,抛出 NoSuchElementException 异常
throw new NoSuchElementException();
}
// 返回第一个元素
return elementData(0);
}
- 该方法首先检查元素数量是否为 0,如果为 0 则抛出
NoSuchElementException
异常。然后返回第一个元素。
4.3.3 lastElement() 方法
lastElement()
方法用于获取 Vector
中的最后一个元素。以下是该方法的源码:
// 获取 Vector 中的最后一个元素
public synchronized E lastElement() {
// 检查元素数量是否为 0
if (elementCount == 0) {
// 如果元素数量为 0,抛出 NoSuchElementException 异常
throw new NoSuchElementException();
}
// 返回最后一个元素
return elementData(elementCount - 1);
}
- 该方法首先检查元素数量是否为 0,如果为 0 则抛出
NoSuchElementException
异常。然后返回最后一个元素。
4.4 修改元素
4.4.1 set(int index, E element) 方法
set(int index, E element)
方法用于修改指定位置的元素。以下是该方法的源码:
// 修改指定位置的元素
public synchronized E set(int index, E element) {
// 检查索引是否越界
if (index >= elementCount)
// 如果索引越界,抛出 ArrayIndexOutOfBoundsException 异常
throw new ArrayIndexOutOfBoundsException(index);
// 获取原来的元素
E oldValue = elementData(index);
// 将指定位置的元素替换为新元素
elementData[index] = element;
// 返回原来的元素
return oldValue;
}
// 获取指定位置的元素
E elementData(int index) {
return (E) elementData[index];
}
- 该方法首先检查索引是否越界,如果越界则抛出
ArrayIndexOutOfBoundsException
异常。然后获取原来的元素,将指定位置的元素替换为新元素,并返回原来的元素。
五、迭代器的实现
5.1 迭代器接口
Vector
实现了 Iterable
接口,因此可以使用 iterator()
方法获取一个迭代器来遍历 Vector
中的元素。以下是 iterator()
方法的源码:
// 获取一个迭代器
public Iterator<E> iterator() {
// 返回一个 Itr 对象
return new Itr();
}
// 内部类 Itr 实现了 Iterator 接口,用于迭代 Vector 中的元素
private class Itr implements Iterator<E> {
// 下一个要返回的元素的索引
int cursor; // index of next element to return
// 上一次返回的元素的索引
int lastRet = -1; // index of last element returned; -1 if no such
// 记录创建迭代器时 Vector 的修改次数,用于并发修改检查
int expectedModCount = modCount;
// 判断是否还有下一个元素
public boolean hasNext() {
// 如果下一个要返回的元素的索引小于元素数量,则返回 true,否则返回 false
return cursor != elementCount;
}
// 获取下一个元素
public E next() {
// 检查是否有并发修改
synchronized (Vector.this) {
checkForComodification();
// 获取下一个要返回的元素的索引
int i = cursor;
if (i >= elementCount)
// 如果索引越界,抛出 NoSuchElementException 异常
throw new NoSuchElementException();
// 更新下一个要返回的元素的索引
cursor = i + 1;
// 返回上一次返回的元素的索引
return elementData(lastRet = i);
}
}
// 删除当前迭代的元素
public void remove() {
if (lastRet == -1)
// 如果上一次返回的元素的索引为 -1,抛出 IllegalStateException 异常
throw new IllegalStateException();
// 检查是否有并发修改
synchronized (Vector.this) {
checkForComodification();
try {
// 调用 Vector 的 remove 方法删除元素
Vector.this.remove(lastRet);
// 更新下一个要返回的元素的索引
cursor = lastRet;
// 将上一次返回的元素的索引置为 -1
lastRet = -1;
// 更新 expectedModCount
expectedModCount = modCount;
} catch (ArrayIndexOutOfBoundsException ex) {
// 如果发生 ArrayIndexOutOfBoundsException 异常,抛出 ConcurrentModificationException 异常
throw new ConcurrentModificationException();
}
}
}
// 检查是否有并发修改
final void checkForComodification() {
if (modCount != expectedModCount)
// 如果 Vector 的修改次数与创建迭代器时记录的修改次数不一致,抛出 ConcurrentModificationException 异常
throw new ConcurrentModificationException();
}
}
iterator()
方法返回一个Itr
对象,用于迭代Vector
中的元素。Itr
类实现了Iterator
接口,提供了以下方法:hasNext()
:判断是否还有下一个元素。next()
:获取下一个元素。在获取元素之前,会检查是否有并发修改,如果有则抛出ConcurrentModificationException
异常。remove()
:删除当前迭代的元素。在删除元素之前,会检查是否有并发修改,如果有则抛出ConcurrentModificationException
异常。checkForComodification()
:检查是否有并发修改,如果Vector
的修改次数与创建迭代器时记录的修改次数不一致,抛出ConcurrentModificationException
异常。
5.2 列表迭代器
Vector
还提供了 listIterator()
方法,用于获取一个列表迭代器,支持双向迭代和元素的插入、删除、修改操作。以下是 listIterator()
方法的源码:
// 获取一个列表迭代器,从指定位置开始迭代
public ListIterator<E> listIterator(int index) {
// 检查索引是否越界
if (index < 0 || index > elementCount)
// 如果索引越界,抛出 IndexOutOfBoundsException 异常
throw new IndexOutOfBoundsException("Index: "+index);
// 返回一个 ListItr 对象
return new ListItr(index);
}
// 获取一个列表迭代器,从第一个元素开始迭代
public ListIterator<E> listIterator() {
// 调用 listIterator(0) 方法,从第一个元素开始迭代
return new ListItr(0);
}
// 内部类 ListItr 实现了 ListIterator 接口,用于列表迭代 Vector 中的元素
private class ListItr extends Itr implements ListIterator<E> {
// 构造函数,从指定位置开始迭代
ListItr(int index) {
// 调用父类的构造函数
super();
// 初始化下一个要返回的元素的索引
cursor = index;
}
// 判断是否还有前一个元素
public boolean hasPrevious() {
// 如果下一个要返回的元素的索引大于 0,则返回 true,否则返回 false
return cursor != 0;
}
// 获取前一个元素的索引
public int nextIndex() {
// 返回下一个要返回的元素的索引
return cursor;
}
// 获取前一个元素
public int previousIndex() {
// 返回下一个要返回的元素的索引减 1
return cursor - 1;
}
// 获取前一个元素
public E previous() {
// 检查是否有并发修改
synchronized (Vector.this) {
checkForComodification();
// 获取前一个元素的索引
int i = cursor - 1;
if (i < 0)
// 如果索引越界,抛出 NoSuchElementException 异常
throw new NoSuchElementException();
// 更新下一个要返回的元素的索引
cursor = i;
// 返回上一次返回的元素的索引
return elementData(lastRet = i);
}
}
// 修改当前迭代的元素
public void set(E e) {
if (lastRet == -1)
// 如果上一次返回的元素的索引为 -1,抛出 IllegalStateException 异常
throw new IllegalStateException();
// 检查是否有并发修改
synchronized (Vector.this) {
checkForComodification();
// 调用 Vector 的 set 方法修改元素
Vector.this.set(lastRet, e);
}
}
// 在当前迭代位置插入一个元素
public void add(E e) {
// 检查是否有并发修改
synchronized (Vector.this) {
checkForComodification();
try {
// 调用 Vector 的 add 方法在当前位置插入元素
Vector.this.add(cursor++, e);
// 将上一次返回的元素的索引置为 -1
lastRet = -1;
// 更新 expectedModCount
expectedModCount = modCount;
} catch (ArrayIndexOutOfBoundsException ex) {
// 如果发生 ArrayIndexOutOfBoundsException 异常,抛出 ConcurrentModificationException 异常
throw new ConcurrentModificationException();
}
}
}
}
listIterator(int index)
方法返回一个ListItr
对象,从指定位置开始迭代。listIterator()
方法调用listIterator(0)
方法,从第一个元素开始迭代。ListItr
类继承自Itr
类,实现了ListIterator
接口,提供了以下方法:hasPrevious()
:判断是否还有前一个元素。nextIndex()
:获取下一个元素的索引。previousIndex()
:获取前一个元素的索引。previous()
:获取前一个元素。在获取元素之前,会检查是否有并发修改,如果有则抛出ConcurrentModificationException
异常。set(E e)
:修改当前迭代的元素。在修改元素之前,会检查是否有并发修改,如果有则抛出ConcurrentModificationException
异常。add(E e)
:在当前迭代位置插入一个元素。在插入元素之前,会检查是否有并发修改,如果有则抛出ConcurrentModificationException
异常。
六、线程安全机制
6.1 同步方法
Vector
是线程安全的,主要通过在方法上使用 synchronized
关键字来实现。例如,add(E e)
、remove(int index)
、get(int index)
等方法都被声明为 synchronized
方法,这意味着同一时间只能有一个线程访问这些方法,从而避免多个线程同时修改 Vector
导致数据不一致的问题。以下是 add(E e)
方法的源码示例:
// 在 Vector 的末尾添加一个元素
public synchronized boolean add(E e) {
// 确保数组的容量足够容纳新元素
modCount++;
ensureCapacityHelper(elementCount + 1);
// 将新元素添加到数组的末尾
elementData[elementCount++] = e;
return true;
}
6.2 性能影响
虽然 synchronized
关键字保证了 Vector
的线程安全,但也带来了一定的性能开销。因为每次调用同步方法时,线程都需要获取锁,这会导致线程之间的竞争和等待,降低了并发性能。在单线程环境下,使用 Vector
会比使用非线程安全的 ArrayList
慢,因为 ArrayList
不需要进行锁的获取和释放操作。
6.3 替代方案
在现代 Java 开发中,如果需要在多线程环境下使用动态数组,可以考虑使用 Collections.synchronizedList
方法将 ArrayList
转换为线程安全的列表,或者使用 CopyOnWriteArrayList
。Collections.synchronizedList
方法通过在 ArrayList
的基础上添加同步机制来实现线程安全,而 CopyOnWriteArrayList
则采用写时复制的策略,在写入操作时创建一个新的数组副本,避免了锁的使用,提高了并发性能。以下是使用 Collections.synchronizedList
方法的示例代码:
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SynchronizedListExample {
public static void main(String[] args) {
// 创建一个 ArrayList
List<String> arrayList = new ArrayList<>();
// 将 ArrayList 转换为线程安全的列表
List<String> synchronizedList = Collections.synchronizedList(arrayList);
// 在多线程环境下使用 synchronizedList
// ...
}
}
七、性能分析
7.1 时间复杂度分析
- 插入操作:
- 在
Vector
的末尾插入元素(add(E e)
):由于Vector
支持动态扩容,在大多数情况下,插入操作的时间复杂度为 O ( 1 ) O(1) O(1)。但当数组需要扩容时,需要进行数组的复制操作,时间复杂度为 O ( n ) O(n) O(n),其中 n n n 为数组的长度。 - 在指定位置插入元素(
add(int index, E element)
):需要将指定位置及其后面的元素向后移动一位,时间复杂度为 O ( n ) O(n) O(n),其中 n n n 为数组的长度。
- 在
- 删除操作:
- 删除指定位置的元素(
remove(int index)
):需要将指定位置后面的元素向前移动一位,时间复杂度为 O ( n ) O(n) O(n),其中 n n n 为数组的长度。 - 删除指定元素(
remove(Object o)
):需要先查找元素的索引,时间复杂度为 O ( n ) O(n) O(n),然后再删除元素,时间复杂度也为 O ( n ) O(n) O(n),因此总的时间复杂度为 O ( n ) O(n) O(n)。
- 删除指定位置的元素(
- 查找操作:
- 获取指定位置的元素(
get(int index)
):由于Vector
支持随机访问,时间复杂度为 O ( 1 ) O(1) O(1)。 - 判断元素是否存在(
contains(Object o)
):需要遍历数组查找元素,时间复杂度为 O ( n ) O(n) O(n),其中 n n n 为数组的长度。
- 获取指定位置的元素(
7.2 空间复杂度分析
Vector
的空间复杂度主要取决于数组的容量和元素的数量。在初始化时,Vector
会分配一个指定初始容量的数组,因此空间复杂度为
O
(
m
)
O(m)
O(m),其中
m
m
m 为初始容量。随着元素的添加,当数组需要扩容时,空间复杂度会随着数组容量的增加而增加。
7.3 与其他集合类的性能对比
- 与
ArrayList
的对比:- 单线程环境:在单线程环境下,由于
ArrayList
不需要进行锁的获取和释放操作,性能通常比Vector
更好。特别是在插入和删除操作频繁的场景下,ArrayList
的性能优势更加明显。 - 多线程环境:在多线程环境下,
Vector
是线程安全的,而ArrayList
不是。如果需要在多线程环境下使用动态数组,可以使用Collections.synchronizedList
方法将ArrayList
转换为线程安全的列表,或者使用CopyOnWriteArrayList
。
- 单线程环境:在单线程环境下,由于
- 与
LinkedList
的对比:- 插入和删除操作:在链表的头部和尾部进行插入和删除操作