深入剖析 Java Vector:从源码洞悉其使用原理

深入剖析 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 功能相似的集合类主要有 ArrayListLinkedList。下面简单对比一下它们的特点:

  • ArrayList:也是一个动态数组实现,与 Vector 不同的是,ArrayList 不是线程安全的。在单线程环境下,由于不需要额外的同步开销,ArrayList 的性能通常比 Vector 更好。
  • LinkedList:采用链表结构实现,与 VectorArrayList 的数组结构不同。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 转换为线程安全的列表,或者使用 CopyOnWriteArrayListCollections.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 的对比
    • 插入和删除操作:在链表的头部和尾部进行插入和删除操作
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Android 小码蜂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值