前言
程序员对哈希算法应该都不陌生,好比业界著名的MD五、SHA、CRC等等;在平常开发中咱们常常用一个Map来装载一些具备(key,value)结构的数据,利用哈希算法O(1)的时间复杂度提升程序处理效率,除此以外,你还知道哈希算法的其余应用场景吗?java
1. 什么是哈希算法?
了解哈希算法的应用场景前,咱们先看下散列(哈希)思想,散列就是把任意长度的输入经过散列算法变换成固定长度的输出,输入称为Key(键),输出为Hash值,即散列值hash(key),散列算法即hash()函数(散列与哈希是对hash的不一样翻译);实际上存储这些散列值的是一个数组,称为散列表,散列表用的是数组支持按照下标随机访问数据的特性,把数据值与数组下标按散列函数作的一一映射,从而实现O(1)的时间复杂度查询;git
1.1 散列冲突
目前的哈希算法MD五、SHA、CRC等都没法作到一个不一样的key对应的散列值都不同的散列函数,即没法避免出现不一样的key映射到同一个值的状况,即出现了散列冲突,并且,由于数组的存储空间有限,也会加大散列冲突的几率。如何解决散列冲突?咱们经常使用的散列冲突解决方法有两类:开放寻址法(open addressing) 和 链表法(chaining)。程序员
1.1.1 开放寻址法
经过线性探测的方法找到散列表中空闲位置,写入hash值:算法
如图,834313在hash表中散列到303432的位置上,出现了冲突,则顺序遍历hash表直到找到空闲位置写入834313;当散列表中空闲位置很少的时候,散列冲突的几率就会大大增长,通常状况下,咱们会尽量保证散列表中有必定比例的空闲槽位,此时,咱们用装载因子来表示空闲位置的多少,计算公式是:散列表的装载因子=填入表中的元素个数/散列表的长度。装载因子越大,说明空闲位置越少,冲突越多,散列表的性能就会降低。数据库
当数据量比较小,装载因子小的时候,适合采用开放寻址法,这也是java中的ThreadLocalMap使用开放寻址法解决散列冲突的缘由。数组
1.1.2 链表法
链表法是一种更经常使用的散列冲突解决办法,也更简单。如图:缓存
在散列表中,每一个桶/槽会对应一条链表,全部散列值相同的元素咱们都放到相同槽位对应的链表中;当散列冲突比较多时,链表的长度也会变长,查询hash值须要遍历链表,这时查询效率就会从O(1)退化成O(n)。安全
这种解决散列冲突的处理方法比较适合大对象、大数据量的散列表,并且,支持更多的优化策略,好比使用红黑树代替链表;jdk1.8为了对HashMap作进一步优化,引入了红黑树,当链表长度太长(默认超过8)时,链表就会转换成红黑树,这时能够利用红黑树快速增删查改的特色,提升HashMap的性能,当红黑树节点个数小于8个时,又将红黑树转化成为链表,由于在数据量比较小的状况下,红黑树要维护平衡,比起链表,性能上的优点并不明显。服务器
2. 哈希算法的应用场景
2.1 安全加密
最经常使用于加密的哈希算法是MD5(MD5 Message-Digest Algorithm)和SHA(Secure Hash Algorithm 安全散列算法),利用hash的特色计算出来的hash值很难反向推导原始数据,从而达到加密的目的。网络
以MD5为例子,哈希值是固定的128位二进制串,最多能表示 2^128 个数据,这个数据已是天文数字了,散列冲突的几率要小于1/2^128,若是但愿经过穷举法来找到跟这个MD5相同的另外一个数据,那耗费的时间也应该是天文数字了,因此在有限的时间内哈希算法仍是很难被破解的,这也就达到了加密效果了。
2.2 数据校验
利用Hash函数对数据敏感的特色,能够用来校验网络传输过程当中的数据是否正确,防止被恶意串改。
2.3 散列函数
利用hash函数相对均匀分布的特色,取hash值做为数据存储的位置值,让数据均匀分布在容器里面。
2.4 负载均衡
经过hash算法,对客户端id地址或者会话id进行计算hash值,将取得的哈希值与服务器列表的大小进行取模运算,最终获得的值就是应该被路由到的服务器编号。
2.5 数据分片
假如咱们有1T的日志文件,里面记录了用户的搜索关键词,咱们想要快速统计出每一个关键词被搜索的次数,该怎么作呢?数据量比较大,很难放到一台机的内存中,即便放到一台机子上,处理时间也会很长,针对这个问题,咱们能够先对数据进行分片,而后采用多台机器处理的方法,来提升处理速度。
具体的思路是:为了提升处理速度,咱们用n台机器并行处理。从搜索记录的日志文件中,依次独处每一个搜索关键词,并经过哈希函数计算哈希值,而后再跟n取模,最终获得的值,就是应该被分配到的机器编号;这样哈希值相同的搜索关键词就被分配到了同一台机器上,每一个机器会分别计算关键词出现的次数,最后合并起来就是最终的结果。实际上,这里的处理过程也是MapReduce的基本设计思想。
2.6 分布式存储
对于海量的数据须要缓存的状况,一台缓存机器确定是不够的,因而,咱们就须要将数据分布在多台机器上。 这时,咱们能够借助前面的分片思想,即经过哈希算法对数据取哈希值,而后对机器个数取模,获得应该存储的缓存机器编号。
可是,若是数据增多,原来的10台机器已没法承受,须要扩容了,这时是若是全部数据都从新计算哈希值,而后从新搬移到正确的机器上,那就至关于全部的缓存数据一会儿都失效了,会穿透缓存回源到数据库,这样就可能发生雪崩效应,压垮数据库。为了新增缓存机器不搬移全部的数据,一致性哈希算法就是比较好的选择了,主要的思想是:假设咱们有kge机器,数据的哈希值范围是[0,Max],咱们将整个范围划分红m个小区间(m远大于k),每一个机器负责m/k个小区间,当有新机器加入时,咱们就将某几个小区间的数据,从原来的机器中搬移到新的机器中,这样,即不用所有从新哈希、搬移数据,也保持了各个机器上数据量的平衡。
3. 写在最后
实际上,哈希算法还有不少其余的应用,好比git commit id等等,不少应用都来自于对算法的理解和扩展,也是基础的数据结构和算法的价值体现,须要咱们在工做中慢慢理解和体会。
参考文档: 《数据结构与算法之美》王争