为什么 Java Vector 被视为遗留类、已过时或已弃用?
处理并发时它的使用是否有效?
如果我不想手动同步对象并且只想使用线程安全集合而不需要制作底层数组的新副本(如 CopyOnWriteArrayList
所做的那样),那么使用 Vector
是否可以?
Stack
是 Vector
的子类,我应该用什么来代替它?
Vector
同步每个单独的操作。这几乎不是你想做的。
通常,您希望同步整个序列的操作。同步单个操作都不太安全(例如,如果您迭代 Vector
,您仍然需要锁定以避免其他任何人同时更改集合,这将导致迭代中的 ConcurrentModificationException
线程)但也更慢(为什么一次就足够了,为什么要反复取出锁)?
当然,即使您不需要,它也有锁定的开销。
基本上,在大多数情况下,这是一种非常有缺陷的同步方法。正如 Brian Henk 先生所指出的,您可以使用诸如 Collections.synchronizedList
之类的调用来装饰集合 - 事实上 Vector
将“调整大小的数组”集合实现与“同步每个操作”结合在一起位是糟糕设计的另一个例子;装饰方法提供了更清晰的关注点分离。
至于 Stack
等价物 - 我会先看看 Deque
/ArrayDeque
。
Vector 是 1.0 的一部分——最初的实现有两个缺点:
<强> 1。命名: 向量实际上只是可以作为数组访问的列表,所以它应该被称为 ArrayList
(它是 Vector
的 Java 1.2 Collections 替代品)。
<强> 2。并发:所有 get()
、set()
方法都是 synchronized
,因此您无法对同步进行细粒度控制。
ArrayList
和 Vector
之间没有太大区别,但您应该使用 ArrayList
。
来自 API 文档。
从 Java 2 平台 v1.2 开始,该类被改进为实现 List 接口,使其成为 Java Collections Framework 的成员。与新的集合实现不同,Vector 是同步的。
ArrayList
、LinkedList
等,它们都实现了接口 List
,所以如果您想使用 List
方法而不必知道底层实现实际上是什么,您只需使用 {3 } 作为方法等的参数。这同样适用于 Map
等的实现者。同时,C++ 确实有一个 std::array
类,它只是 C 样式静态长度数组的基于模板的替代品。
除了已经说明的关于使用 Vector 的答案外,Vector 还有一堆与 List 接口不同的枚举和元素检索方法,开发人员(尤其是那些在 1.2 之前学习 Java 的人)如果他们在代码。尽管枚举速度更快,但它们不会检查集合是否在迭代期间被修改,这可能会导致问题,并且考虑到可能会选择 Vector 进行同步 - 伴随着来自多个线程的访问,这使得它成为一个特别有害的问题。这些方法的使用也将大量代码与 Vector 耦合,因此不容易用不同的 List 实现替换它。
您可以使用 java.util.Collection
中的 synchronizedCollection/List 方法从非线程安全集合中获取线程安全集合。
java.util.Stack
继承了 java.util.Vector
的同步开销,这通常是不合理的。
不过,它继承的远不止这些。 java.util.Stack extends java.util.Vector
在面向对象设计中是一个错误。纯粹主义者会注意到,除了传统上与堆栈相关的操作(即:push、pop、peek、size)之外,它还提供了许多方法。还可以执行 search
、elementAt
、setElementAt
、remove
和许多其他随机访问操作。避免使用 Stack
的非堆栈操作基本上取决于用户。
由于这些性能和 OOP 设计原因,JavaDoc for java.util.Stack
建议将 ArrayDeque
作为自然替代品。 (双端队列不仅仅是一个堆栈,但至少它仅限于操作两端,而不是提供对所有内容的随机访问。)