杰瑞科技汇

Vector与List有何区别?

核心结论先行

一句话概括:Vector 是一个古老的、线程安全的类,而 List 是一个接口,它定义了列表的行为,ArrayListLinkedListList 接口最常用的两个实现,在现代 Java 编程中,除非有非常特殊的并发需求,否则应该优先使用 ArrayList 而不是 Vector


从概念和层次结构上理解

要明白它们在 Java 集合框架中的位置和角色是完全不同的。

List (接口)

  • 是什么List 是一个接口(Interface),位于 java.util.List
  • 作用:它定义了一个有序的、可以包含重复元素的集合的规范或契约,任何实现了 List 接口的类,都必须提供 add(), get(), remove(), size() 等方法。
  • 实现类ArrayList, LinkedList, Vector, Stack 等都是 List 接口的实现类。
  • 类比:你可以把 List 理解成“列表”这个概念本身,它描述了列表应该具备哪些功能。

Vector与List有何区别?-图1

(图片来源: Oracle Java Docs)

Vector (类)

  • 是什么Vector 是一个具体的类(Concrete Class),位于 java.util.Vector
  • 作用:它是一个实现了 List 接口的类,并且从 JDK 1.0 就存在了,它提供了一种动态数组的功能。
  • 关键特性Vector 最大的特点是线程安全(Thread-Safe)。
  • 类比Vector 是“列表”这个概念的一个具体实现,而且是一个自带“安全锁”的实现。

详细对比表格

特性 Vector List (接口) ArrayList (最常用实现)
类型 具体类 接口 具体类
线程安全 ,所有公共方法都使用 synchronized 关键字,是同步的。 不保证List 接口本身没有定义任何同步机制。 非同步,不是线程安全的。
性能 较低,因为每个方法都有同步开销(锁竞争),在单线程或多线程环境下性能都不如 ArrayList - ,在非并发场景下,没有同步开销,性能最好。
扩容机制 容量翻倍,当内部数组满了,新数组的大小是原来的 2 倍 - 容量大约增加 50%,当内部数组满了,新数组的大小是原来的 5 倍,这种策略通常能更好地利用内存。
历史/遗留 遗留类,从 JDK 1.0 存在,是 Java Collections Framework (JCF) 出现之前的产物。 核心接口,JCF 的核心组成部分,自 JDK 1.2 引入。 现代首选,JCF 引入后,作为 Vector 的非线程安全替代品。
使用场景 极少,在需要强一致性且对性能要求不高的旧代码中可能见到,现代开发几乎被 Collections.synchronizedList()CopyOnWriteArrayList 替代。 声明变量,当你只关心列表功能,不关心具体实现时,应使用 List list = new ArrayList<>(); 通用场景,绝大多数情况下,当你需要一个列表时,都应首选 ArrayList
遍历方式 支持 Iterator,也支持遗留的 Enumeration 支持 Iterator, for-each 循环等。 支持 Iterator, for-each 循环等。

关键差异深入解析

1 线程安全 vs. 性能

这是两者最核心的区别。

  • Vector 的线程安全实现

    // Vector 源码中的 add 方法 (简化版)
    public synchronized E add(E e) {
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return e;
    }

    synchronized 关键字确保了在同一时间只有一个线程可以执行 add 方法,这在多线程环境下可以防止数据不一致,但也带来了巨大的性能代价,即使是在单线程环境下,这个锁也是完全多余的。

  • ArrayList 的高性能

    // ArrayList 源码中的 add 方法 (简化版)
    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 没有同步
        elementData[size++] = e;
        return true;
    }

    由于没有同步开销,ArrayList 的操作速度远快于 Vector

2 如何在需要线程安全时选择?

既然 Vector 性能不好,那我们如何在需要线程安全的列表时做选择呢?

使用 Collections.synchronizedList() 包装器

这是最常见的方式,它将一个非线程安全的列表(如 ArrayList)包装成一个线程安全的列表。

List<String> list = Collections.synchronizedList(new ArrayList<>());
// 遍历时需要手动同步
synchronized (list) {
    for (String item : list) {
        // do something
    }
}

这种方式的好处是:

  1. 灵活性:你可以选择任意 List 实现(如 ArrayList)作为基础。
  2. 性能更好:只有在修改列表(增删改)时才进行同步,遍历时由用户手动同步,比 Vector 的“处处同步”更高效。

使用 CopyOnWriteArrayList

这是一个专门为并发遍历优化的列表,它的特点是写时复制,当修改列表时,它会复制一份底层数组,在副本上进行修改,然后替换引用,这使得读操作完全无锁,性能极高。

List<String> threadSafeList = new CopyOnWriteArrayList<>();

适用场景读多写少的场景,一个事件监听器列表,大部分时间只是读取监听器,偶尔会添加或移除。

继续使用 Vector (不推荐) 除非你在维护一个非常古老的系统,Vector 的使用模式已经深入其中,否则没有理由再主动选择它。


代码示例

1 现代 Java 推荐用法

import java.util.ArrayList;
import java.util.List;
public class ModernListExample {
    public static void main(String[] args) {
        // 1. 声明为接口,实现为 ArrayList (最佳实践)
        // 这意味着你未来可以轻松切换为 LinkedList,而无需修改调用处的代码。
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Charlie");
        System.out.println("List: " + names);
        System.out.println("First element: " + names.get(0));
        // 2. 如果需要线程安全,使用包装器
        List<Integer> numbers = Collections.synchronizedList(new ArrayList<>());
        numbers.add(1);
        numbers.add(2);
        numbers.add(3);
        // 注意:同步的 List 在迭代时也需要手动同步
        synchronized (numbers) {
            for (Integer num : numbers) {
                System.out.println(num);
            }
        }
    }
}

2 不推荐的用法 (过时的 Vector)

import java.util.Vector;
public class LegacyVectorExample {
    public static void main(String[] args) {
        // 3. 除非有特殊理由,否则不要使用 Vector
        Vector<String> legacyVector = new Vector<>();
        legacyVector.add("One");
        legacyVector.add("Two");
        legacyVector.add("Three");
        System.out.println("Vector: " + legacyVector);
        System.out.println("First element: " + legacyVector.get(0));
    }
}

总结与建议

List (接口) Vector (类) ArrayList (类)
角色 规范 过时的、线程安全的实现 现代、高性能的通用实现
线程安全 不保证 是 (性能差)
何时用 总是用来声明变量,表示这是一个列表。 几乎不用,维护旧代码时可能遇到。 几乎总是用,99% 的列表需求都应该用它。
替代方案 - Collections.synchronizedList() -

最终建议

  1. 永远优先使用 List 接口来声明你的变量,List<String> myList = ...,这体现了面向接口编程的思想,提高了代码的灵活性和可维护性。
  2. 在实现列表时,首选 ArrayList,它提供了在大多数情况下最优的性能。
  3. 只有在明确需要并发安全时,才考虑使用 Collections.synchronizedList()CopyOnWriteArrayList,而不是 Vector
  4. Vector 视为一个历史遗留物,了解它即可,但在新代码中尽量避免使用。
分享:
扫描分享到社交APP
上一篇
下一篇