Java中Hashtable、HashMap、ConcurrentHashMap之间的区别

前言

Hashtable是java早期发布时提供的一种键值映射的数据结构,而HashMap产生于JDK1.2。对于在单线程中使用,由于Hashtable为保证线程安全在许多关键方法中都加锁以至于效率相比于HashMap低对于在多线程中我们又有ConcurrentHashMap在jdk1.8之前通过加分段锁,在jdk1.8通过直接给每个哈希桶(链表分配一个锁)实现线程安全 效率相比于Hashtable高 。所以目前Hashtable基本上已经被弃用了。

总结: 在多线程中我们可以使用ConcurrentHashMap,在单线程中我们可以使用Hashtable。

1.Hashtable与ConcurrentHashMap比较

二者都是线程安全的,都可以在多线程环境下使用,但是二者的实现线程安全的方式不同,以下是将介绍二者的加锁不同之处。

1.1 Hashtable

Hashtable通过对关键方法加上synchronized关键字,这就相当于对Hashtable对象本身加锁,此时会出现以下情况:

  1. 一个Hashtable只有一把锁,如果多个线程访问同一个 Hashtable中的任何数据时都会发生锁冲突。
  2. size 属性通过 synchronized 来控制同步,一旦触发扩容, 就由该线程完成整个扩容过程.。这个过程会涉及到大量的元素拷贝, 效率会非常低。

如图所示为Hashtable的加锁方式:
在这里插入图片描述

1.2 ConcurrentHashMap

在jdk 1.8之前,ConcurrentHashMap通过加分段锁实现线程安全,简单来说就是将若干个哈希桶分成一个段,针对每个段加锁,在jdk1.8就改为直接对每个哈希桶加锁。
相比于Hashtable进行的优化:

  1. 读操作没有加锁(使用volatile 保证从内存读取结果), 只对写操作进行加锁.。
  2. 加锁的方式是用 synchronized, 但是不是锁整个对象, 而是 “锁桶” (用每个链表的头结点作为锁对象), 大大降低了锁冲突的概率。
  3. 充分利用 CAS 特性。比如 size 属性通过 CAS 来更新,避免出现重量级锁的情况。
  4. 优化了扩容方式: 化整为零。
    1. 发现需要扩容的线程, 只需要创建一个新的数组, 同时只搬几个元素过去。
    2. 扩容期间, 新老数组同时存在,后续每个来操作 ConcurrentHashMap 的线程, 都会参与搬家的过程,每个操作负责搬运一小部分元素。
    3. 搬完最后一个元素再把老数组删掉
    4. 插入只往新数组中插入,查找需要同时查询数组和老数组。

在这里插入图片描述

2. Hashtable与HashMap的比较

  1. 底层实现不同
    Hashtable和HashMap都可以用于单线程情况,HashMap底层在jdk1.7以前是通过哈希表(数组+链表的方式)实现来解决哈希冲突,但是在jdk1.8之后, HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间而Hashtable 没有变化。

关于哈希表的概念以及哈希冲突,Hashtable的存储过程可以参照我的另一篇博客HashMap和HashSet之间的关系

  1. 继承的父类不同

    1. HashTable是继承自Dictionary(已被废弃)
      public class Hashtable<K, V> extends Dictionary<K, V> implements Map<K, V>, Cloneable, Serializable
    2. HashMap是继承自AbstractMap类
      public class HashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable
    3. HashMap和Hashtable都实现了map、Cloneable、Serializable这三个接口。
  2. 键值不同

    1. Hashtable不支持key为null或者value为null。
    2. HashMap中,键可以为null但是这样的键只有一个,可以有一个或多个键所对应的值为null。
      注意: 当get()方法返回null值时,可能是 HashMap中没有该键,也可能使该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
  3. 线程安全
    Hashtable线程安全,HashMap线程不安全。

  4. 遍历方式不同
    Hashtable、HashMap可以使用Iterator。而Hashtable还支持使用Enumeration的方式 。

  5. 初始容量不同

    1. Hashtable的初始长度是11,之后每次扩充容量变为之前的2n+1.
    2. HashMap的初始长度为16,之后每次扩充变为原来的两倍。
    3. 如果创建时给定了容量初始值,那么Hashtable会直接使用你给定的大小,而HashMap会将其扩充为最接近该值的2的幂次方大小

    注意:二者负载因子均为0.75

  6. 计算哈希值的方法不同

    1. Hashtable使用对象的hashCode。HashCode是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值。然后再使用除留余数发来获得终的位置。 除法运算是非常耗费时间的。效率很低
    2. HashMap提高计算效率,将哈希表的大小固定为了2的幂,这样在取模预算时,不需要做除法,只需要做位运算。位运算比除法的效率要高很多。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值