点击上方“朱小厮的博客”,选择“设为星标”

后台回复"k8s"领取阿里云《深入浅出k8s.pdf》

来源:rrd.me/gEtR3

最近对hash有了更多深入的理解。这里也写篇文章专门来聊聊hash。

Hash是一种常见的数据结构或者说计算方法,以其O(1)的时间算法复杂度闻名于世。曾有人说,如果世界上只有一种数据结构,那么我选择hash,足见hash的地位及牛逼之处,而代码编写中hash也屡见不鲜,因为它实在是太常见太好用了。

但是实际使用过程中,基本的hash是远远不够的,按照用途,对hash其实还有如下需求:

关于java中hash的数据结构:

1.并发安全。

对这个需求,java中有了HashTable,为了进一步提升性能,于是有了使用分段锁的ConcurrentHashMap,亦不做赘述。

2.大数据hash。

传统的HashMap中除了key, value外,每个entry还要存16个byte的class header,4byte的hash值,以及8byte的指向下一个元素的指针,这样的结构在遇到大数据量时就会更加耗内存,更容易导致GC。

由对象头过大可以看出来,只要能够有一种结构消灭这个额外的entry对象,则此处将大大减少内存的消耗。

一种可行的方式是:采用二级索引保存的方式,第一级索引由Short2ShortMap保存一个short为key且short为value的Map结构,第二级索引则由许多数组构成,这些数组负责将被消灭value这个Object拆解为基本类型并用多个数组保存,而一级索引的value保存的value正是二级数组的index。通过这种变换,消灭了额外的entry对象从而大幅减少内存。需要注意的是,这种方式适用于使用了大量HashMap,但是每个Map内数据量较小的情况(受short的限制只有3w多index),如果每个Map内数据量也比较大,可以考虑Int2IntMap,当然,这样减少内存占用的效果就不如Short2ShortMap了。

3.其他。

ImmutableMap,Guava库,在初始化完毕后就没法再put做改变了。

SortedMap,Guava库,数据会按key做字母化排序。

BiMap,Guava库,创建完之后可以使用inverse将value和key颠倒过来,前提是保证value也是唯一的。

MultiMap,Guava库,可以对每个key关联多个值,并且可以很方便的对list进行分组。

关于hash的一些解决方案:

Hash冲突。

众所周知,解决hash冲突最好的办法自然是提升hash table的总数量(即N的大小),如果待存放元素的数量k远小于N,则hash后有更大概率占据空槽,而冲突越少则性能越好,本质上,这是一种以空间换时间的方式。然而现实中,存储空间也很宝贵,任何公司都很难接受让大量空间浪费。于是,便出现了尽可能增加空间占用但不过分降低性能的hash。

布谷hash。

布谷hash是一种解决冲突的方法。不同于线性探测,开放定址这样的常规方法,布谷hash借鉴了布谷鸟占人巢穴生子的寓意。其算法比较简单,采用两个(或多个)hash函数F1和F2,put操作时用F1或F2计算hashcode并定位,如果任意位置为空,则插入;否则挤占其中一个位置,并将被挤占的元素拿出并重复该过程;而get操作则让人比较困惑,到底采用哪个函数来get值呢?实际上布谷hash需要在value中存放key值,这样对于两个函数get到的值只要判断中间key是否正确就可以确认其对应的hash函数。布谷hash在二维时空间利用率较高,约为80%-90%。下图是对put操作的一个表示。

bloomfilter。

布隆过滤器是一种占小空间且效率很高的算法,通常用来解决垃圾邮件识别,缓存击穿及日活计算等场景。bloomfilter只能判断一个元素可能在其中或者一个元素一定不在其中。他的算法也采用多个hash函数,如下例,某数据A经过x函数可以映射到4,9两个位置,经过z函数可以映射到9,14两个位置,经过y函数可以映射到14,19两个位置。于是基本的增加操作便可以将这几个对应位置的值置为1;对于基本的查找操作,则对A进行hash后找到其所有对应位置,发现其所有对应位置都是1,则表示A很可能存在,为什么不能确定呢,因为有可能这些位置并不是对A进行hash后对应的位置,有可能是插入了BCDE等数据而这些数据刚好覆盖了A的所有位置而导致的,所以发现全1仅仅能判断其可能存在;但是一旦有任意对应位置为0,则表示A一定不存在。对于基本的删除和更新操作,布隆过滤器是不支持的,本质原因是位置是多数据共享的,任何对数据的逆向操作都会导致其他数据的不准。布隆过滤器在Guava中有现成的实现。

Count–min sketch。

Count-min sketch旨在解决流式大数据下做计数统计时间空间复杂度过高的问题。设想这样一个场景,线上数据源源不断的进来,现在我们需要去统计cache中每个ip请求的大致数量,从而确定哪个ip来的请求是hot的。碰到这个问题,可能本能的会想用HashMap,用ip作为key,用总count当做value。但实际上当数据量足够大时,各种噩梦就来了,比如每台机器内存非常高(对应上面说到的大数据hash带来的问题),hash冲突也变高,rehash成本也会迅速增加,并且在实时响应的要求下,时间上就很可能无法满足需求,Count-min sketch算法就是为此而生的。

count-min sketch算法思想比较简单,采用n个数组以及n个hash函数,对同一个数据用不同的hash函数做hash,分配到这n个数组不同的位置,存值时这个位置所在的value加1,取值时取这n个位置最小值,则此最小值大致接近实际总count数,且总是大于等于实际的总count数。为啥要取最小值,并且为啥结果总是大于等于实际总count数呢,原因其实与bloomfilter比较像,有可能有其他的hash也落到了该位置并加了count。参考下图。在java中,著名的caffeine缓存框架中的W-TinyLFU就用的Count-min sketch来记录访问频率。

4.hash分散。

大多数情况下,希望hash之后的结果越分散越无规律越好。

Murmur hash。Murmur哈希是一种比大多数算法更为分散更无规律的算法。

java中的hash算法称为Horner,简单表示就是

for (int i = 0; i < str.length(); i++) { hash = 31*hash + str.charAt[i]; }

实际计算时经常使用移位操作。

Murmur的意思是multiply and rotate,主要优点是速度快且hash值足够分散,目前已经在各大框架广泛使用,比如redis,memcache,cassandra,lucene,如下是其简单表示。

x *= m; x = rotate_left(x,r);

5.hash聚集。

少数情况下,希望通过hash能让相似的内容在hash过后仍然相似,而不是一点改动便面目全非。

simhash。simhash是一种局部敏感hash,对于google百度这样的大搜索公司,用空间向量去计算文档相似度显得既慢又笨重,simhash用一种相似则海明距离近的方式巧妙而快速的解决了文档相似的比较。这对hash提出了另一种不同的要求,以往hash函数的目的是为了足够分散,而这里却希望hash后呈现一定的规律,实际上个人觉得这更像是一种编码,根据这种编码规则,相似的文档在hash值上的海明距离更近。

6.其他特殊hash。

一致性hash。

一致性hash主要是为了解决传统的取模为主的hash将数据分配到n台服务器之后,服务器再扩容或缩容所带来的所有数据需要重新计算hash的问题。这种情况对于线上某些重要的服务往往是不可接受的。于是一致性hash出现了,它通过将hash值空间预先分配到一个超级大的虚拟节点上,再通过实体节点就近接管虚拟节点来解决映射问题。如图,这个超级大的虚拟节点即是2^32个,真正的的实体节点只有4个,由于顺时针就近映射,每个实体节点都将接管落入前面一个实体节点以后的所有虚拟节点的值,这样每次扩容时只会影响最多一个节点。一致性hash基本人尽皆知,这里就不列举资料了。

Perfect hash。

perfect hash目的是为了实现完全无冲突的hash。perfect hash分为两种,一种是静态hash,一种是动态hash;对于静态hash而言,一个最好的例子就是数组,比如总的值有10个,取hash值后分别映射到3,8,13,18,22,44,53,63,78,92这10个位置,则我们用一个长度为100的数组可以实现该值域的静态perfect hash。但是你可能会发现有多余的位置并没有被用上,如果能实现长度10的数组完美映射这10个数字,则称之为最小完美hash。动态perfect hash一般比较麻烦,需要做二次hash映射并要第二次映射不会冲突,有兴趣可以查阅相关资料。

GeoHash。


GeoHash是比较特殊的hash应用,主要是用来快速定位。其原理相对简单(实现起来有不少细节)。主要就是将每一级的地图划分为32块,即每一级用5bit来标识(为啥是5bit,因为最后用base32的编码方式,每个字母或数字5bit),每次缩放一级则用另一个字母或数字标识,最终能得到一串字符串wx4gjk32kfrx,从而一级一级定位直到最小那一级。如划分10级,则最后字符串长度为4,范围到20km,如划分20级,则最后字符串长度为8,范围可以精确到19m。

想知道更多?描下面的二维码关注我

后台回复”加群“获取公众号专属群聊入口

当当优惠码福利来一波!当当全场自营图书5折,用优惠码:TASEMU(长按复制),满200(原价400)再减30,相当于170=400,四折多一点。使用渠道:当当小程序或当当APP。使用时间:4/10-4/23。目前优惠码只有少量了,且不会再增加。

【原创系列 | 精彩推荐】

  • Paxos、Raft不是一致性算法嘛?

  • 越说越迷糊的CAP

  • 面试官居然问我Raft为什么会叫做Raft!

  • 面试官给我挖坑:URI中的//有什么用

  • 网关Zuul科普

  • 网关Spring Cloud Gateway科普

  • 分布式事务科普——初识篇

  • 分布式事务科普——终结篇

  • 面试官给我挖坑:a[i][j]和a[j][i]有什么区别?

  • Nginx架构原理科普

朕已阅 

如果世界上只有一种数据结构,那么我选择 hash相关推荐

  1. 汉语是世界上唯一一种面向对象的高级语言

    2019独角兽企业重金招聘Python工程师标准>>> 在 网上看到过很多有关汉语和英语比较的文章,他们写的都很不错,并且列举了大量的事例 来证明汉语或者英语是优秀的. 不过,我想用 ...

  2. 洛克菲勒:世界上只有两种人头脑聪明...

    洛克菲勒:被人称为"石油大王".美国第一位十亿富豪与全球首富.创办芝加哥大学.洛克菲勒写给儿子的信共有38封,这些信真实记录了洛克菲勒创造财富神话的种种业绩.从这些信中我们不仅可以 ...

  3. 世界上有两种公众号,我坚持做第二种

    世界上有两种公众号,一种喜欢追热点,热点过去,文章的价值也就随风而逝.另外一种不追热点,写一些能够经受时间考验的文章. 区分它们的方法很简单: 你看看他会不会定期发文章目录. 相比而言,第二种比较吃亏 ...

  4. 世界上究竟有多少种云计算?这其实是个伪命题

    世界上究竟有多少种云计算?这其实是个伪命题 两个多月前,阿里云的一句:"中国只有两种云,一种是拿来主义的云,一种是自主可控的飞天云."引发了业界的广泛吐槽,不过很多人嘴上虽然不服气 ...

  5. 世界上有10种人,一种懂二进制的,另一种不懂

    前一段时间看到网上有个笑话--世界上有10种人,一种懂二进制的,另一种不懂,这时有人问这不是才2种人吗? 哈哈哈不知你反应过来了吗?这个笑话也侧面反映了理解二进制系统的重要性,请细读下文,待我一步步揭 ...

  6. 分布式系统原理-世界上只有一种共识算法,那就是Paxos

    分布式系统原理系列目录 分布式系统的麻烦 副本与一致性 为什么需要一个分布式共识算法 世界上只有一种共识算法,那就是Paxos CAP定理,说起来一句话,实际坑不少 BASE,可用性高于强一致性 分布 ...

  7. 世界上到底有多少种编程语言

    今天在找选题的时候,发现一篇<世界上最不流行的编程语言>.程序员常常讨论世界上最流行的编程语言有哪些,各种编程语言排行榜也会定时发布出来,例如Python.JavaScript.Java这 ...

  8. 世界上只有两种黑客:俄罗斯黑客和普通黑客

    大家都知道,前段时间,几乎各个国家都在制裁俄罗斯.索尼公司也宣布制裁了俄罗斯,结果俄罗斯黑客就直接把PS5攻破了.除了破解下载权限之外,还把源代码给公开了.紧接着欧美时下最火的付费游戏也被一一破解,像 ...

  9. 世界上一共有多少种编程语言?

    今天在网上闲逛,看到了一篇很有趣的文章--台湾的Jserv的深入淺出 Hello World>,更有趣的是其中提到的世界上各种语言写的hello world的大集合: http://www.nt ...

最新文章

  1. Tomcat-公布WEB应用
  2. 官宣!微软宣布桌面版 Edge将基于Chromium进行开发\n
  3. 【转】PHP foreach 小结
  4. Proxmox系列:简单实现虚拟机迁移
  5. E-UTRA channel bandwidths per operating band (36.101)
  6. opencv-python教程学习系列2-读取/显示/保存图像
  7. Python 正则 —— 捕获与分组
  8. EhCache复制:RMI与JGroups
  9. Android 系统(18)---Handler,MessageQueue与Looper关系
  10. 操作系统思考 第六章 内存管理
  11. java 日期年度 35变2035_连接IBM MQ原因码报2035的错误解决办法
  12. mp4 avc格式_sps_pps
  13. 医药行业数据分析软件(含非付费)--对比分析
  14. C++ 自定义函数(全)
  15. 实现一个打点计时器,要求 1、从 start 到 eThd(包含 start 和 eThd),每隔 100 毫秒 coThsole.log 一个数字,每次数字增幅为 1 2、返回的对象中需要包含一个
  16. mysql查看被锁住的表
  17. WiFi分析仪在Android9.0上不能用解决方案
  18. 达梦常见故障模拟与恢复
  19. 和平精英分数计算机制,和平精英段位对应的积分完整一览 和平精英多少分上王牌...
  20. ML与Docker:《Deploy Machine Learning Pipeline on the cloud using Docker Container使用Docker容器在云端部署机器学习管道

热门文章

  1. html登录注册的正则,怎么用html5编写用户注册验证程序
  2. K8S之HELM详细介绍
  3. NIO--Buffer
  4. dbeaver导出建表语句_细致入微:如何使用数据泵导出表的部分列数据
  5. MQTT.fx连接aliyun阿里云的方法
  6. 深度解析利用ES6进行Promise封装总结
  7. 有效的字母异位词---简单
  8. JavaScript进行UTF-8编码与解码
  9. 上海网域CEO肖确伟:IDC精细化运营探讨
  10. 浅谈EntityFramework框架的使用