JDK的弃儿:Vector、Stack、Hashtable、Enumeration

简介: 随着JDK的发展,一些设计缺陷或者性能不足的类库难免会被淘汰,最常见的就是Vector、Stack、HashTable和Enumeration了。

随着JDK的发展,一些设计缺陷或者性能不足的类库难免会被淘汰,最常见的就是Vector、Stack、HashTable和Enumeration了。


Vector(@since 1.0)

首先看看Vector的UML类图,可以看出,他是一个与ArrayList有着相同继承体系的类,大致功能也和ArrayList一样。Vector与ArrayList最大的不同点在于它是线程安全的,因为其内部几乎所有方法都用了synchronized来修饰。但是,Synchronized是重量级锁,读写操作也没有做适当的并发优化,已经被并发性更好的CopyOnWriteArrayList取代了。所以,当不要求线程安全时,自然会选择ArrayList,如果要求线程安全,往往也会选择CopyOnWriteArrayList或者Collections.synchronizedList()。

这里写图片描述

Stack(@since 1.0)

Stack是Vector的子类,其内部的方法也都是通过无脑加synchronized来实现的,所以虽然线程安全,但是并发性不高。当不要求线程安全时,会选择LinkedList或者ArrayList(LinkedList的API更接近栈的操作,所以最佳选择是LinkedList),当要求线程安全时,我们会用java.util.concurrent包下的某些类。
再多句嘴,虽然LinkedList的API比较接近栈的操作,但是暴露了许多用不着的方法,这会带来危险。解决方法是编写一个LinkedList的包装类,只暴露与栈相关的方法。

**
 * 包装{@code LinkedList},使其仅暴露与栈相关的方法
 */
public class Stack<T> {
    private LinkedList<T> list;
    public Stack() {
        list = new LinkedList<>();
    }
    public void push(T item) {
        list.push(item);
    }
    public T pop() {
        return list.pop();
    }
    public T peek() {
        return list.peek();
    }
    public boolean isEmpty() {
        return list.isEmpty();
    }
    @Override
    public String toString() {
        return list.toString();
    }
}

Hashtable(@since JDK1.0)

首先看看Hashtable的UML类图,关键点是其实现了Map接口,所以它是一个存储键值对的容器。通过查看源码,我们知道,其是一个线程安全的类,而且还是用synchronized来实现的,所以并发性不高。所以,当面对不要求线程安全的应用场景时我们会用HashMap代替,要求线程安全的应用场景我们往往也会用ConcurrentHashMap或者Collections.synchronizedMap()来代替。
这里写图片描述
再再多句嘴,它与HashMap还有一个比较出名的不同点,就是它的散列表实现算法是用线性探测法实现的,该算法要求key不能为null,不然删除键值对时会出问题。另外还要求value不能为null。具体见源码。

public synchronized V put(K key, V value) {
    // Make sure the value is not null
    if (value == null) { //value不能为null
        throw new NullPointerException();
    }
    // Makes sure the key is not already in the hashtable.
    Entry<?,?> tab[] = table;
    int hash = key.hashCode(); //key不可以为null
    int index = (hash & 0x7FFFFFFF) % tab.length;
    @SuppressWarnings("unchecked")
    Entry<K,V> entry = (Entry<K,V>)tab[index];
    for(; entry != null ; entry = entry.next) {
        if ((entry.hash == hash) && entry.key.equals(key)) {
            V old = entry.value;
            entry.value = value;
            return old;
        }
    }
    addEntry(hash, key, value, index);
    return null;
}
public synchronized boolean contains(Object value) {
    if (value == null) {
        throw new NullPointerException();//value不能为null
    }
    Entry<?,?> tab[] = table;
    for (int i = tab.length ; i-- > 0 ;) {
        for (Entry<?,?> e = tab[i] ; e != null ; e = e.next) {
            if (e.value.equals(value)) {
                return true;
            }
        }
    }
    return false;
}

Enumeration(@since JDK1.0)

Enumeration是否是JDK的“弃儿”其实是有争论的,有人认为,有了Iterator的存在,Enumeration存在的意义就仅仅是兼容老API(比如Vector、Hashtable)了;又有人认为,Enumeration提供了比Iterator更明确的语义(明确不希望对象被执行移除操作)。

1     * NOTE: The functionality of this interface is duplicated by the Iterator
2     * interface.  In addition, Iterator adds an optional remove operation, and
3     * has shorter method names.  New implementations should consider using
4     * Iterator in preference to Enumeration.
5     这个接口的功能与Iterator接口重复了。此外,Iteraotr还有一个可选的remove()方法和更短的名字,新应用应该优先考虑Iterator

总之,根据API的说明,我们得知,新应用应优先考虑Iterator接口。
再再再多句嘴,万一要面对Enumeration,又想有个关于迭代的统一接口,可以使用适配器模式来处理Enumeration。

** 
 * 把Enumeration接口转换成Iterator接口的适配器  
 * 适配器模式中的角色 - adaptor 
 */  
public class EnumerationIterator<E> implements Iterator<E> {  
    /** 
     * 被适配的接口 
     * 适配器模式中的角色 - adaptee  
     */  
    private Enumeration<E> enums;  
    public EnumerationIterator(Enumeration<E> enums) {  
        this.enums = enums;  
    }  
    @Override  
    public boolean hasNext() {  
        return enums.hasMoreElements();  
    }  
    @Override  
    public E next() {  
        return enums.nextElement();  
    }  
    /** 
     * 因为Enumeration接口不支持remove操作,所以这里简单地抛出异常 
     */  
    @Override  
    public void remove() {  
        throw new UnsupportedOperationException();  
    }  
}

总结

Vector、Stack、Hashtable由于其自身的设计不足而且又有替代的工具,所以在新项目中已难寻其踪。Iterator的强大功能也使Enumeration处境尴尬,也已经很少见到了。

本文转自

JDK的弃儿:Vector、Stack、Hashtable、Enumeration-付大石

引用

Java中,遍历数据结构Enumeration和Iterator相比有什么优劣?
一个简单的适配器模式例子

目录
相关文章
|
3天前
|
Java 大数据 Maven
jdk17出现错误无法初始化主类 和NoClassDefFoundError:Vector的解决方法
在JDK 17中使用孵化模块Vector时遇到的“无法初始化主类”和“NoClassDefFoundError: Vector”错误的解决方法,通过在Maven项目中配置编译插件、编写测试代码、配置Java运行时环境,并提供了运行项目的示例。
39 9
jdk17出现错误无法初始化主类 和NoClassDefFoundError:Vector的解决方法
|
5月前
|
存储 Java 索引
【亮剑】Java中的并发容器ConcurrentHashMap,它在JDK1.5中引入,用于替换HashTable和SynchronizedMap
【4月更文挑战第30天】本文介绍了Java中的并发容器ConcurrentHashMap,它在JDK1.5中引入,用于替换HashTable和SynchronizedMap。文章展示了创建、添加、获取、删除和遍历元素的基本用法。ConcurrentHashMap的内部实现基于分段锁,每个段是一个独立的Hash表,通过分段锁实现并发控制。每个段内部采用数组+链表/红黑树的数据结构,当冲突过多时转为红黑树优化查询。此外,它有扩容机制,当元素超过阈值时,会逐段扩容并翻倍Segment数量,以保持高性能的并发访问。
55 0
|
5月前
|
存储 安全 Java
Hashtable和HashMap:差异,数据结构概述,以及JDK的影响
Hashtable和HashMap:差异,数据结构概述,以及JDK的影响
44 0
|
存储 安全 Java
Java集合简单了解——基于JDK1.8中LinkedHashMap、TreeMap、Hashtable、Properties的实现原理
Java集合简单了解——基于JDK1.8中LinkedHashMap、TreeMap、Hashtable、Properties的实现原理
Java集合简单了解——基于JDK1.8中LinkedHashMap、TreeMap、Hashtable、Properties的实现原理
|
存储 安全 Java
Java集合源码剖析——基于JDK1.8中Vector的实现原理
Java集合源码剖析——基于JDK1.8中Vector的实现原理
Java集合源码剖析——基于JDK1.8中Vector的实现原理
|
安全 容器
Hashtable源码分析(基于jdk1.8,推荐)
Hashtable也算是集合系列中一个比较重要的集合类了,不过在介绍Hashtable的时候,总是不可避免的谈到HashMap,在面试的时候Hashtable往往也会结合HashMap一块来问。这篇文章就来好好地分析一下Hashtable
149 0
Hashtable源码分析(基于jdk1.8,推荐)
|
安全 Java vr&ar
Java进阶:【集合】1.8 jdk Arraylist和Vector集合源码分析
Java进阶:【集合】1.8 jdk Arraylist和Vector集合源码分析
Java进阶:【集合】1.8 jdk Arraylist和Vector集合源码分析
|
安全 算法 Java
一文读懂JDK1.7,JDK1.8,JDK1.9的hashmap,hashtable,concurrenthashmap及他们的区别
本篇为威力加强升级版本,读到最后,有惊吓 1:hashmap简介(如下,数组-链表形式) HashMap的存储结构 图中,紫色部分即代表哈希表,也称为哈希数组(默认数组大小是16,每对key-value键值对其实是存在map的内部类entry里的),数组的每个元素都是一个单链表的头节点,跟着的绿色链表是用来解决冲突的,如果不同的key映射到了数组的同一位置处,就将其放入单链表中。