Java集合之HashMap/hashTable详解

 更新时间:2023年09月21日 08:50:32   作者:X-TIE  
这篇文章主要介绍了Java集合之HashMap/hashTable详解,Map是映射键值的对象,map不能包含重复键:每个键最多只能映射一个值,它模拟了数学函数的抽象,需要的朋友可以参考下

HashMap/hashTable详解

Map是映射键值的对象。map不能包含重复键:每个键最多只能映射一个值。它模拟了数学函数的抽象。

Map接口包括基本操作的方法(如put、get、remove、containsKey、containsValue、size和empty)、批量操作(如putAll和clear)和集合视图(如keySet、entrySet和values)。Java平台包含三个通用的映射实现:HashMap、TreeMap和LinkedHashMap。

它们的行为和性能与Set接口部分中描述的HashSet、TreeSet和LinkedHashSet类似。

下面从HashMap和hashTable两个容器分别介绍对比介绍一下。

下面我来分别看一下HashMap 和 hashTable 在无参构造函数实例化的具体实例:

由上图我们可以看到HashMap的无参数构造函数new 了一个:容量为16,加载因子为0.75,阈值为12的容器。

而hashTable的无参数构造函数则new 了一个:容量为11,加载因子为0.75,阈值为8的容器。

其中阈值为容量和加载因子的乘积,意思是如果容器到了这个值,那么就要实施扩容的机制了。下面我们看一下这两个容器到了阈值分别是如何扩容的呢?

首先是HashMap的扩容机制:

从源码上看,容器扩大了原容器的length*2倍。必须是2的冥。(2的几次方)

里面还有一个判断如果原来容器的容量已经达到了最大值,那么就把阈值调整到最大值,然后把原数组数据映射到新的更大的数组当中。这也就是说当数据量过多并且知道最大值的时候为了避免哈希表被重新散列(防止内部数据结构频繁被重新构建)。

然后我们看一下hashTable是如何扩容的:

原容器的大小乘以2+1,保证得到的数据是一个奇数。那么到这了我们考虑一下为什么table扩容要求是奇数,而map扩容必须是2的冥呢?

那么我们下面说一下HashMap的扩容机制以及确认元素位置的源码,来分析一下为什么设计成2的冥:

通过位运算符保证初始容量一定是2的冥

为了防止hash碰撞,在Entry数组(单链表)中为了保证每一个位置只有一个元素,通过hash%table.length=bucketIndex,bucketIndex为元素具体的位置,这样能够均匀的分布到容器的各个位置且不会有重复的。对集合操作效率也高。那么我们看一下源码中是如何找到元素具体的位置的:

为了减少碰撞HashMap是做了二次hash运算的。

h为最后计算的hash值,length为容器的容量。假设容量 = 16,我们计算一个hash值,来看一下h具体值为,并且我们计算一下indexFor的值是什么:

可以看到具体的hash值和在容器中的一个位置信息。

然后我们看一下,巧合的是根据我们的计算h & (length - 1) == h % length两个等式正好相等。且

这个的位运算的效率更高,这个应该就是容量必须为2的幂的原因。保证了数据分散的均匀。

并且通过二次hash减少碰撞,那么什么是碰撞呢?碰撞就是两个数计算出来的hash值一样,且equals e1.equals(e2)不相等,这样在一个hash位置上就会存储多个链表。在取值或者删除数据元素的时候效率比较低。

上面就是说的HashMap的容量为什么是2的冥的原因,下面来介绍一下hashTable的初始容量为什么是11,以及扩容机制?

hashTable的key获取hash为直接返回的当前key的hashCode值例如:如果是一个String的lisi返回3322014。

通过拆分lisi为char数组元素,且每个值拿到ASCII值的十进制。31*hash + 当前码值。

直接计算当前hash & long int的最大值%当前容器的容量,获得具体在容器中的位置。

int newCapacity = (oldCapacity << 1) + 1;这个是hashTable的一个扩容计算规则:保证了扩容后容量始终为奇数。

那么hashTable的扩容容量始终保证为奇数呢?

首先我猜测跟他的确认地址是有关系的,在就是由于hashTable全程加了同步锁为线程安全的,为了性能更高的操作容器才会这么设置,这块如果有小伙伴能讲解的比较清楚也欢迎评论交流指导。

最后总结一下:哈希表的大小为素数时,简单的取模哈希的结果会更加均匀,所以单从这一点上看,HashTable的哈希表大小选择,似乎更高明些。但另一方面我们又知道,在取模计算时,如果模数是2的幂,那么我们可以直接使用位运算来得到结果,效率要大大高于做除法。所以从hash计算的效率上,又是HashMap更胜一筹。之所以不一样是因为HashMap用的位移运算确认具体位置,而hashTable是直接用的模。(事实就是HashMap为了加快hash的速度,将哈希表的大小固定为了2的幂。当然这引入了哈希分布不均匀的问题,所以HashMap为解决这问题,又对hash算法做了一些改动。HashMap和HashTable在计算hash时都用到了一个叫hashSeed的变量。这是因为映射到同一个hash桶内的Entry对象,是以链表的形式存在的,而链表的查询效率比较低,所以HashMap/HashTable的效率对哈希冲突非常敏感,所以可以额外开启一个可选hash(hashSeed),从而减少哈希冲突。)

在结尾总结性的补充一下这个hashTable和HashMap的异同:

1.首先父类不同。hashTable的父类是Dictionary<K,V>,HashMap的父类是AbstractMap<K,V>.

2.HashMap是支持null键和null值的,而HashTable在遇到null时,会抛出NullPointerException异常。

3.初始化大小不同,扩容机制不同。

4.hashTable为线程安全的,方法级别的强制同步。HashMap非线程安全的。所以HashMap效率性能要高。

相同点:都实现了Map<K,V>接口。

到此这篇关于Java集合之HashMap/hashTable详解的文章就介绍到这了,更多相关HashMap/hashTable详解内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • java高并发下CopyOnWriteArrayList替代ArrayList

    java高并发下CopyOnWriteArrayList替代ArrayList

    这篇文章主要为大家介绍了java高并发下CopyOnWriteArrayList替代ArrayList的使用示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • Tomcat安装配置及Eclipse配置详解

    Tomcat安装配置及Eclipse配置详解

    给大家介绍一下Tomcat安装配置及Eclipse配置的全部图文过程,如果你对这个还有不明白,一起跟着小编学习下。
    2017-11-11
  • MybatisPlus代码生成器的使用方法详解

    MybatisPlus代码生成器的使用方法详解

    在这里我将展示如何自动生成实体类、控制层、服务层、mapper等代码,这些基础的代码全部不需要我们手动创建,由MybatisPlus自动帮我们完成,我们只需要告诉MybatisPlus怎么生成这些代码就可以了,在此之前我们需要配置好测试的环境,数据库和表数据 ,需要的朋友可以参考下
    2021-06-06
  • 基于Jackson实现API接口数据脱敏的示例详解

    基于Jackson实现API接口数据脱敏的示例详解

    用户的一些敏感数据,例如手机号、邮箱、身份证等信息,在数据库以明文存储,但在接口返回数据给浏览器(或三方客户端)时,希望对这些敏感数据进行脱敏,所以本文就给大家介绍以恶如何利用Jackson实现API接口数据脱敏,需要的朋友可以参考下
    2023-08-08
  • 解决kafka:org.apache.kafka.common.errors.TimeoutException问题

    解决kafka:org.apache.kafka.common.errors.TimeoutException问题

    这篇文章主要介绍了解决kafka:org.apache.kafka.common.errors.TimeoutException问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • Java实现InputStream的任意拷贝方式

    Java实现InputStream的任意拷贝方式

    这篇文章主要介绍了Java实现InputStream的任意拷贝方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-10-10
  • SpringBoot的HandlerInterceptor中依赖注入为null问题

    SpringBoot的HandlerInterceptor中依赖注入为null问题

    这篇文章主要介绍了SpringBoot的HandlerInterceptor中依赖注入为null问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • maven的pom文件与打包详解

    maven的pom文件与打包详解

    pom文件定于了一个maven项目的maven配置,一般pom文件的放在项目或者模块的根目录下。本文详细的介绍了pom文件配置,感兴趣的可以了解一下
    2021-08-08
  • Spring Boot + Kotlin整合MyBatis的方法教程

    Spring Boot + Kotlin整合MyBatis的方法教程

    前几天由于工作需要,便开始学习了kotlin,java基础扎实学起来也还算比较快,对于kotlin这个编程语言自然是比java有趣一些,下面这篇文章主要给大家介绍了关于Spring Boot + Kotlin整合MyBatis的方法教程,需要的朋友可以参考下。
    2018-01-01
  • Java判断字符串是否为IP地址的方法

    Java判断字符串是否为IP地址的方法

    这篇文章主要为大家详细介绍了Java判断字符串是否为IP地址的方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-08-08

最新评论