HashMap 和 Hashtable 的区别

74 阅读2分钟

HashMap 和 Hashtable 的区别

1. 父类不同

  • HashMap:继承自 AbstractMap 类。
  • Hashtable:继承自 Dictionary 类。

两者都实现了 MapCloneable(可复制)、Serializable(可序列化)这三个接口。

2. 对外提供的接口不同

  • Hashtable:比 HashMap 多提供了 elements()contains() 两个方法。
    • elements() 方法继承自 Dictionary 类,用于返回此 Hashtable 中的 value 的枚举。
    • contains() 方法判断该 Hashtable 是否包含传入的 value,其作用与 containsValue() 一致。实际上,containsValue() 只是调用了 contains() 方法。

3. 对 null 的支持不同

  • Hashtablekeyvalue 都不能为 null
  • HashMapkey 可以为 null,但只能有一个这样的 key,因为必须保证 key 的唯一性;可以有多个 key 值对应的 valuenull

4. 线程安全性不同

  • HashMap:线程不安全,在多线程并发的环境下,可能会产生问题,如死锁等。需要开发人员自行处理多线程的安全问题。
  • Hashtable:线程安全,每个方法上都有 synchronized 关键字,因此可以直接用于多线程环境中。

尽管 HashMap 是线程不安全的,但其效率远高于 Hashtable。这是因为大部分使用场景都是单线程的。当需要多线程操作时,可以使用线程安全的 ConcurrentHashMapConcurrentHashMap 的效率比 Hashtable 高很多,因为它使用了分段锁,并不对整个数据进行锁定。

5. 初始容量和扩充容量大小不同

  • HashMap:默认初始容量为 16,每次扩充容量为原来的两倍。
  • Hashtable:默认初始容量为 11,每次扩充容量为原来的两倍加一。

6. 计算 hash 值的方法不同

  • HashMap:使用自身的 hash 算法,默认采用对象的 hashCode 值,并对其进行再加工(扰动函数),以减少冲突。

    static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    
  • Hashtable:直接使用对象的 hashCode 值,计算方式较简单。

    int hash = key.hashCode();
    

总结

特性HashMapHashtable
父类AbstractMapDictionary
额外方法elements(), contains()
对 null 的支持keyvalue 可以为 nullkeyvalue 不能为 null
线程安全性线程不安全线程安全
初始容量和扩充容量初始容量 16,扩充容量为原来的两倍初始容量 11,扩充容量为原来的两倍加一
hash 值计算使用自定义的 hash 算法使用对象的 hashCode

选择使用哪种数据结构应根据具体的使用场景来决定。如果在多线程环境下,需要使用线程安全的集合,可以选择 ConcurrentHashMap,而不是 Hashtable