最详细Java---HashMap 与 Hashtable 的区别?

Posted AnWen~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最详细Java---HashMap 与 Hashtable 的区别?相关的知识,希望对你有一定的参考价值。

  • HashMap产生于JDK1.2,HashTable产生于JDK1.1

    Hashtable继承自Dictionary类,而HashMap继承自AbstractMap类。二者都实现了Map接口。

    • Dictionary就是为了记录键值的一一对应关系,已经过时
    • Map将Key映射到Value对象,这个就是为了取代Dictionary抽象类
  • Null Key && Null Value
    • HashMap允许是支持null键和null值的,允许一个null键和多个null值

    • HashTable不允许null键值,在遇到null时,会抛出NullPointerException异常。

    • HashMap在实现时对null做了特殊处理,将null的hashCode值定为了0,从而将其存放在哈希表的第0个bucket中。

      static final int hash(Object key) 
          int h;
          return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
      
      
  • 数据结构
    • HashMap和HashTable都使用哈希表来存储键值对。在数据结构上是基本相同的,都创建了一个**继承自Map.Entry的私有的内部类Entry,每一个Entry对象表示存储在哈希表中的一个键值对**
    • Node和Entry封装内容差不多,只是名字不同。HashMap采用Node。HashTable采用Entry
    • HashMap/HashTable内部创建**有一个Entry类型的引用数组**,用来表示哈希表,数组的长度,即是哈希桶的数量。
    • 而数组的每一个元素都是一个Entry引用,从Entry对象的属性里,也可以看出其是链表的节点,每一个Entry对象内部又含有另一个Entry对象的引用
    • jdk1.8 中,哈希表存储采用数组+链表+红黑树实现,当链表长度大于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。
  • 线程安全性
    • HashMap是非synchronized的,而Hashtable是synchronized的。这说明Hashtable是线程安全的,而且多个线程可以共享一个Hashtable;
    • 因为 Hashtable 内部的方法基本都经过synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap(采用CAS---乐观锁和Synchronized---悲观锁),只锁住当前桶 吧!);
  • hash不同

    • HashMap添加元素时,是使用自定义的哈希算法,而HashTable是直接采用key的hashCode()

    • HashTable 计算hash,直接用key的hashCode(),再取模,取模运算效率不高

    • HashMap,先得到hashCode,在高位计算,最后使用优化的取模运算

    • 这个方法非常巧妙,它通过**h & (table.length -1)来得到该对象的保存位,而HashMap底层数组的长度总是2的n次方,这是HashMap在速度上的优化。当length总是2的n次方时**,h& (length-1)运算等价于对length取模,也就是h%length,但是&比%具有更高的效率。

    • 在JDK1.8的实现中,优化了高位运算的算法,通过hashCode()的高16位异或低16位实现的:(h = k.hashCode()) ^ (h >>> 16),主要是从速度、功效、质量来考虑的,这么做可以在数组table的length比较小的时候,也能保证考虑到高低Bit都参与到Hash的计算中,同时不会有太大的开销

      int hash = key.hashCode();
      int index = (hash & 0x7FFFFFFF) % tab.length;
      // &0x7FFFFFFF的目的是为了将负的hash值转化为正值,因为hash值有可能为负数,而&0x7FFFFFFF后,只有符号外改变,而后面的位都不变。
      ====================HashMap============
      static final int hash(Object key) 
              int h;
              return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
          
      i = (n - 1) & hash
      
  • 初始化
    • HashTable在不指定容量的情况下的默认容量为11,而HashMap为16
    • Hashtable不要求**底层数组的容量一定要为2的整数次幂,而HashMap则要求一定为2的整数次幂**。
    • 创建时如果不指定容量初始值,Hashtable 默认的初始大小为 11,之后每次扩充,容量变为原来的 2n+1
    • HashMap 默认的初始化大小为 16。之后每次扩充,容量变为原来的 2 倍
    • 创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为 2 的幂次方大小HashMap 中的tableSizeFor()方法保证,下面给出了源代码)。
    • 也就是说 HashMap 总是使用 2 的幂作为哈希表的大小,后面会介绍到为什么是 2 的幂次方。
  • 遍历方式
    • HashMap只支持Iterator遍历
    • HashTable支持Iterator和Enumeration两种方式遍历
  • 迭代器不同
    • HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的迭代器(enumerator)却不是fail-fast的。
    • 所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。而Hashtable 则不会。
  • contains方法
    • HashMap把Hashtable的contains方法改成containsValue和containsKey;
    • Hashtable则保留了contains,containsValue和containsKey三个方法,其中contains和containsValue功能相同。
  • 同步性
    • Hashtable是同步(synchronized)的,适用于多线程环境,
    • hashmap不是同步的,适用于单线程环境。
    • 多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。
    • **由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。**如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
  • 效率
    • 因为线程安全的问题,HashMap 要比 Hashtable 效率高一点。
    • 另外,Hashtable 基本被淘汰,不要在代码中使用它;

以上是关于最详细Java---HashMap 与 Hashtable 的区别?的主要内容,如果未能解决你的问题,请参考以下文章

最详细Java---HashMap 与 Hashtable 的区别?

JAVA HashMap详细介绍和示例

Java:HashMap原理与设计缘由

Java HashMap详细介绍和使用示例(正在整理学习中)

Java HashMap详细讲解

java8 HashMap源码 详细研读