Vector 与 ArrayList 区别

面试官问:"Vector 和 ArrayList 有什么区别?"

候选人小杜答:"Vector 是线程安全的,ArrayList 不是。"

面试官追问:"那 Vector 怎么保证线程安全的?"

小杜说:"用 synchronized。"

面试官追问:"synchronized 加在哪些方法上?性能怎么样?为什么现在不推荐用 Vector 了?"

小杜支支吾吾答不上来。

【面试官心理】 这道题看似简单,实际上在测试候选人对线程安全和性能的理解。能说出 Vector 用 synchronized 的占 80%,能解释 synchronized 的代价的占 40%,能说出替代方案的只有 20%。


一、核心区别概览 🔴

维度VectorArrayList
线程安全synchronized(同步方法)不安全
性能较低(有锁竞争)较高
扩容策略capacity * 2capacity * 1.5 + 1
迭代器迭代中修改抛 ConcurrentModificationException
引入版本JDK 1.0JDK 1.2

二、线程安全实现对比 🔴

2.1 Vector 的 synchronized

// Vector 的方法都是 synchronized
public synchronized boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

public synchronized E get(int index) {
    if (index >= elementCount)
        throw new ArrayIndexOutOfBoundsException(index);
    return elementData[index];
}

public synchronized int size() {
    return elementCount;
}

2.2 ArrayList 的无锁实现

// ArrayList 没有 synchronized
public boolean add(E e) {
    modCount++;
    ensureCapacityHelper(elementCount + 1);
    elementData[elementCount++] = e;
    return true;
}

public E get(int index) {
    rangeCheck(index);
    return elementData[index];
}

性能差异:synchronized 有锁竞争开销,多线程并发访问 Vector 性能急剧下降。


三、扩容策略对比 🟡

3.1 Vector 扩容 capacity * 2

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // Vector 扩容 2 倍
    int newCapacity = oldCapacity + oldCapacity;
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    elementData = Arrays.copyOf(elementData, newCapacity);
}

3.2 ArrayList 扩容 capacity * 1.5 + 1

private void grow(int minCapacity) {
    int oldCapacity = elementData.length;
    // ArrayList 扩容 1.5 倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    elementData = Arrays.copyOf(elementData, newCapacity);
}

对比

初始容量Vector 扩容后ArrayList 扩容后
102010 → 15
100200100 → 150

四、为什么 Vector 已经过时?🟡

4.1 synchronized 的代价

// 单线程环境下,Vector 和 ArrayList 性能几乎相同
// 多线程环境下,Vector 因为锁竞争性能急剧下降

// 示例:100 个线程并发写入
Vector: 1000ms
ArrayList(非线程安全): 100ms
CopyOnWriteArrayList: 200ms

4.2 替代方案

场景替代方案
需要线程安全Collections.synchronizedList(new ArrayList<>())
读多写少CopyOnWriteArrayList
需要高性能并发ConcurrentLinkedQueue / ConcurrentHashMap

Collections.synchronizedList 示例

List<String> list = Collections.synchronizedList(new ArrayList<>());
// 所有操作都是 synchronized,但锁粒度更细
synchronized(list) {
    for (String s : list) {
        System.out.println(s);
    }
}

五、Vector 的适用场景 🟡

5.1 遗留代码兼容

很多老项目还在用 Vector,完全替换成本高。

5.2 需要线程安全且简单场景

// Vector 的 API 和 ArrayList 几乎一样,简单替换可行
List<String> list = new Vector<>();  // 代替 ArrayList

5.3 Stack 类(JDK 官方推荐替代)

// JDK 官方文档推荐
Deque<Integer> stack = new ArrayDeque<>();  // 代替 Stack

// Stack 继承自 Vector,已过时
public class Stack<E> extends Vector<E> { }

六、面试官追问 🔴

面试官:"synchronized 加在方法上,和加在代码块上有什么区别?"

标准回答

  • synchronized 方法:锁住整个对象,所有访问该方法都需要竞争锁
  • synchronized 代码块:只锁住代码块,其他方法可以并发访问

面试官追问:"Vector 的迭代器是线程安全的吗?"

标准回答

  • 不是。Vector 的 iterator()listIterator() 返回的迭代器不是线程安全的
  • 迭代过程中修改会抛 ConcurrentModificationException
  • 需要用 Collections.synchronizedList() 包装后,在同步块中遍历

面试官追问:"那你说什么场景下可以用 Vector?"

标准回答

  • 新代码:不推荐用,直接用 ArrayList
  • 老代码:如果已经在用,可以继续用
  • 简单工具类:不需要并发的地方,Vector 和 ArrayList 性能一样

七、总结

观点说明
Vector 是否线程安全是(synchronized 方法)
为什么过时synchronized 性能差,多线程场景有更好选择
替代方案Collections.synchronizedList() / CopyOnWriteArrayList
JDK 官方推荐栈ArrayDeque 代替 Stack
新代码选型ArrayList(非并发),需要并发用 ConcurrentHashMap