Java中的HashMap相信大家都不陌生,也是大家编程时最常用的数据结构之一,各种面试题更是恨不得掘地三尺的去问HashMap、HashTable、ConcurrentHashMap,无论面试题多么刁钻的问,只要我们真正的掌握了它的设计思想,便可以不变应万变,hold住所有的面试题了。

本文主要包含以下内容,力求深入浅出一步一步彻底明白HashMap的设计思想:

  1. 数组的优势
  2. 数组是特殊的键值对
  3. Hash函数
  4. Hash冲突
  5. 此时再看HashMap源码

文章干货内容较多,建议大家“收藏”后持续阅读!

关注“java架构设计”,阅读更多技术干货文章!

数组的优势

整型数组

上图是一个含有8个元素的整型数组,数组下标从0到7,

如果我们要获取第四个元素的值,直接a[3]就可以了,时间复杂度为O(1),

如果我们要将第四个元素的值替换为36,直接a[3]=36就可以了,时间复杂度也是O(1),

也就是说基于下标的随机访问和赋值数组元素的时间复杂度都是O(1),无论这个数组是多大(在内存充足的条件下),这是数组的优势之一。

数组不够,我们还需要键值对

通过上面的简单描述,我们可以知道数组可以通过“下标”来获取数组中的指定元素,但是这个下标只能是正整数,且从0开始。

但是如果我们想通过一个浮点数、一个字符串、一个对象来获取对应的元素呢?也就是所谓的键值对,是不是数组就满足不了了?

我们可以把数组看做是一种特殊的键值对,key就是数组的下标,value就是下标对应的元素:

键值对

所以我们要求KV数据结构里面的key是一个对象,而不仅仅只能是数组中的一个下标。因此我们需要创造出一种数据结构,他至少需要具有以下的特性:

  1. O(1)复杂度的访问任何一个key对应的值
  2. 这个key可以支持整型、浮点、字符串、对象等任何类型的数据

第一个特性要求我们需要一个像数组下标一样的整型数字来快速访问数组。

第二个特性要求我们的下标不限制只能是整型。

显然我们需要对key做一些特殊处理,这个时候Hash函数就上场了。

Hash函数

哈希函数的作用就是通过哈希算法把任意类型的key转换成固定类型、固定长度的散列值,也就是我们所期望的数组下标(整型)。

因此哈希函数需要具有如下的特征:

  1. 相同的内容经过哈希算法计算后输出结果一致;
  2. 不同的内容经过哈希算法计算后输出不同的结果,但也可能会出现相同的输出值(即哈希碰撞);

因此,一个优秀的哈希算法是不同的内容经过哈希计算后输出的结果具有分布均匀的特点,也就是低碰撞率。

同时,计算速度必须要快!

比较出名的哈希算法有time33算法、Murmurhash算法,这些算法都在追求更好的均匀分布和更快的计算速度。

Java8中java.lang.String类的hashCode方法实现:

java.lang.String#hashCode()

现在来写一个简单的测试类来测试Java中的String类实现的hashCode:

输出:

s1.hashCode=92599395s2.hashCode=92599395s3.hashCode=92599396

可以看出来s1和s2是相同的字符串,输出了相同的hash值,s3和s1、s2不同,输出的hash值也不同,但是也很接近。

说明java采用的hash算法分散性不好,如果用Murmurhash算法,差异就会很大,即哈希算法的剧烈度大,感兴趣的同学可以用Guava中Murmurhash方法试验一下。

通过Hash算法,我们可以计算出任何一种数据类型的哈希值且为整型,这样就满足了数组的下标必须为整数的要求了。

但是又来了一个问题,通过上面的实验我们拿到的hashCode值很大,无法作为数组的下标,否则我们的数组占用的内存就太大了!

所以就采用了根据hashCode取余的方式,比如Java中的HashMap默认size是16,那么92599395%16=3,那么实际上abcde这个字符串就存储在HashMap数组中下标为3的地方。

Hash冲突

上面已经讲过Hash算法无法做到完全均匀分布,也就是说可能会有那么两个不一样的字符串经过hash计算后得到相同的值,此时两个不同的字符串都得对应同一个数组下标上,这就造成了所谓的Hash冲突。

因此,为了解决Hash冲突问题,我们需要下标对应的元素不再仅仅是当前对应的字符串了,而应该是当前的字符串再加上它的next节点的对象地址,这样的一个对象应该如下:

当根据key去找值时候,先计算出key的hash值再取余得到数组的下标,然后根据下标获取到元素,再判断该元素的key是否和当前的key相等(如何判断相等?equals方法!),如果不相等,继续取next节点,继续判断。

说到这里大家如果对HashMap熟悉的话,是不是发现这其实就是HashMap的简单版,一个数组+链表的实现:

再看HashMap源码

如果你已经看到这里,那么我相信你一定已经了解了HashMap的结构了,那么请回去看HashMap的源码吧,你会发现原来是这么简单!这个时候你已经达到了“看山不是山”的境界了!面试官问你任何关于HashMap的问题相信你都能回答了。

搞清楚了HashMap之后,HashTable和HashMap的区别?ConcurrentHashMap是线程安全的基于分段锁的HashMap?为了优化链表的性能,当链表的数超过8之后就变成平衡树了等等。。。

后面做的事情要么是为了线程安全要么就是为了性能。

如果你这么去理解HashMap就会发现它真的很简单的更多相关文章

  1. JSON:如果你愿意一层一层剥开我的心,你会发现...这里水很深——深入理解JSON

    我们先来看一个JS中常见的JS对象序列化成JSON字符串的问题,请问,以下JS对象通过JSON.stringify后的字符串是怎样的?先不要急着复制粘贴到控制台,先自己打开一个代码编辑器或者纸,写写看 ...

  2. 深入理解HashMap

    转自:http://annegu.iteye.com/blog/539465 Hashmap是一种非常常用的.应用广泛的数据类型,最近研究到相关的内容,就正好复习一下.网上关于hashmap的文章很多 ...

  3. 集合之深入理解HashMap

    Hashmap是一种非常常用的.应用广泛的数据类型 1.hashmap的数据结构 要知道hashmap是什么,首先要搞清楚它的数据结构,在java编程语言中,最基本的结构就是两种,一个是数组,另外一个 ...

  4. 深入理解HashMap(原理,查找,扩容)

    面试的时候闻到了Hashmap的扩容机制,之前只看到了Hasmap的实现机制,补一下基础知识,讲的非常好 原文链接: http://www.iteye.com/topic/539465 Hashmap ...

  5. 深入理解HashMap(及hash函数的真正巧妙之处)

    原文地址:http://www.iteye.com/topic/539465 Hashmap是一种非常常用的.应用广泛的数据类型,最近研究到相关的内容,就正好复习一下.网上关于hashmap的文章很多 ...

  6. 深入理解HashMap(jdk7)

    HashMap的结构图示 ​ jdk1.7的HashMap采用数组+单链表实现,尽管定义了hash函数来避免冲突,但因为数组长度有限,还是会出现两个不同的Key经过计算后在数组中的位置一样,1.7版本 ...

  7. Map 综述(一):彻头彻尾理解 HashMap

    转载自:https://blog.csdn.net/justloveyou_/article/details/62893086 摘要: HashMap是Map族中最为常用的一种,也是 Java Col ...

  8. 从逆向的角度去理解C++虚函数表

    很久没有写过文章了,自己一直是做C/C++开发的,我一直认为,作为一个C/C++程序员,如果能够好好学一下汇编和逆向分析,那么对于我们去理解C/C++将会有很大的帮助,因为程序中所有的奥秘都藏在汇编中 ...

  9. JAVA中的数据结构 - 真正的去理解红黑树

    一, 红黑树所处数据结构的位置: 在JDK源码中, 有treeMap和JDK8的HashMap都用到了红黑树去存储 红黑树可以看成B树的一种: 从二叉树看,红黑树是一颗相对平衡的二叉树 二叉树--&g ...

随机推荐

  1. mongodb 在PHP中常见问题及解决方法

    1.$in needs an array 解决:查询用到in操作的时候,说in操作对应的不是我一个数组,或者数组索引不是以0开始的 方法:array_values重新生成一个索引为0开始的数组即可 $ ...

  2. Spring MVC工作原理及源码解析(二)DispatcherServlet实现原理及源码解析

    1.DispatcherServlet 处理流程 从上一篇文章中Spring MVC原理图中我们可以看出:DispatcherServlet 在 Spring MVC框架 中处于核心位置,它负责协调和 ...

  3. 有哪些适合中小企业使用的PaaS平台?

    对于中小企业来说,在业务上同样需要工作流.应用平台来进行支持,但是,面对诸如ERP等动辄好几十万的费用来说,完全是在增加运营成本.如何解决中小企业对于业务应用.工作流管理的需求问题呢?使用PaaS低代 ...

  4. 读HikariCP源码学Java(一)-- 通过ConcurrentBag类学习并发编程思想

    前言 ConcurrentBag是HikariCP中实现的一个池化资源的并发管理类.它是一个高性能的生产者-消费者队列. ConcurrentBag的并发性能优于LinkedBlockingQueue ...

  5. 从零搭建springboot服务03-redis消息订阅

    愿历尽千帆,归来仍是少年 1.所需依赖 <!-- Redis依赖 --> <dependency> <groupId>org.springframework.boo ...

  6. Advanced Archive Password Recovery (ARCHPR) 是一个强大的压缩包密码破解工具,适用于ZIP和RAR档案的高度优化的口令恢复工具。

    RAR压缩文件密码破解工具是一款简单易用的RAR文档和ZIP文档密码破解软件,如果你不小心忘了解压密码或是下载的RAR文件需要密码,那么均可以使用本软件进行暴力破解.不管WinRAR /RAR 的密码 ...

  7. Windows 电脑的四种运行状态工作状态 (Working), S0 睡眠状态 (Sleep), S1 或 S3 休眠状态 (Hibernate), S4 关机状态 (Shutdown), S5

    == Windows 电脑的四种运行状态 == 这四种运行状态(或称电源状态)是: 工作状态 (Working), S0 睡眠状态 (Sleep), S1 或 S3 休眠状态 (Hibernate), ...

  8. Msf--永恒之蓝 ms17_010

    |>>>中华人民共和国网络安全法<<<|警告:请勿用于非法用途,后果自负! 简介 一.概述 永恒之蓝是指2017年4月14日晚,黑客团体Shadow Brokers ...

  9. 单臂路由实现不同vlan间通信

    单臂路由实现不同vlan间通信 拓扑图 PC配置 PC1 :192.168.1.1 vlan10 192.168.1.254 PC2 :192.168.2.1 vlan20 192.168.2.254 ...

  10. Docker网络(5)

    一.docker网络介绍 大量的互联网应用服务需要多个服务组件,这往往需要多个容器之间通过网络通信进行相互配合 docker 网络从覆盖范围可分为单个 host 上的容器网络和跨多个 host 的网络 ...