目录

1. hash表的原理与实现
2. Java中的hashcode()方法
3. Collection中HashMap的应用
4. Bloom Filter在海量数据中的应用

1.hash表原理与实现

根据key-value而直接进行访问的数据结构。把关键字key通过某种映射函数映射到表中的某个位置来访问,来提高查找的速度。映射函数就称之为hash函数。存放映射的数组称之为hash表,散列表是根据关键字直接求出地址的查找方法,其查找的期望时间是O(1)。

冲突(碰撞collision):对不同的关键字key ,得到相同的表的位置。这种情况称之为碰撞(collision)。几种解决冲突的方法:

开放定址法:hash表由m个元素组成,其地址是0到m-1,在向表中插入新纪录的时候,如果通过hash函数映射得到的地址已经被占用。那么通过探查下个空位置来存放这个新纪录。采用的方式就是可以通过这个公式
hi=(h(key)+di)%m 1≤i≤m-1
 ① 、 h(key)为散列函数,di为增量序列,m为表长。
 
 ② 、 h(key)是初始的探查位置,后续的探查位置依次是hl,h2,…,hm-1,即h(key),hl,h2,…,hm-1形成了一个探查序列。
 
 ③ 、 散列表的装填因子α= 填入表中的元素个数 / 散列表的长度。开放定址发要求hash表的装填因子α<=1
 
拉链法:将所有产生冲突的放在同一个单链表中,也就是说对每个hash地址都建立一个单链表。这样一个hash地址就可以被多个关键字key共享。是不是觉得很熟悉,没错HashMap的底层实现与之很类似。一个Entry数组,每个数组元素相当于一个链表的头元素。

2.Java中的hashcode方法

首先API中的解释说明Returns a hash code value for the object. This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable .意思就是说,hashcode方法返回这个对象的hash(散列)值,这个方法为hash表提供一些便利(优点),例如,java.util.Hashtable提供的hash表。
关于hashcode的一些协定:
 1、在同一次 应用执行期间,多次调用一个对象的hashcode方法,其返回的integer值必须保持一致。在某个应用程序的一次执行到另一次执行,调用一个对象的hashcode值在两次执行过程中,不必保持一致。
 
 2、根据equals(Object )方法,如果两个对象相等,那么分别调用两个对象的hashcode方法返回的integer值必须一致。
 
 3、以下情况不是必需的:如果根据equals(Object)方法,如果两个对象不相等,那么这两个对象调用hashcode方法必定返回不一致的值。但是,程序员应该晓得,为不相等的对象生成不同的整数结果可以提高hash表的性能。(换句话说就是如果两个对象不相等,那么hashcode的返回值可能相等也可能不相等,但最后不相等)。
 
举个例子

package com.cn.hn.test;
import java.util.Date;
public class HashcodeSimple {public static void main(String[] args){String test1 = "123";String test2 = test1;System.out.println("test1==test2?"+test1.equals(test2));System.out.println("String:"+test1.hashCode());Date d1 =new Date();System.out.println("java.util.Date:"+d1.hashCode());}
}

第一次执行结果

test1==test2?true
String:48690
java.util.Date:1732896453

第二次执行结果

test1==test2?true
String:48690
java.util.Date:1732948949

这里呢:String类型的hashcode两次返回的值都保持了一致。原因是因为字符串常量“123”,如果字符串常量池中没有,则会把“123”字符串插入到常量池中,如果有的话直接返回引用。因为String的hashcode方法跟String对象底层的数组有关,字符串常量不变,底层的数组也没有变,自然得到的hash值也没有改变

public int hashCode() {int h = hash;int len = count;if (h == 0 && len > 0) {int off = offset;char val[] = value;for (int i = 0; i < len; i++) {h = 31*h + val[off++];}hash = h;}return h;}

再看java.util.Date其实现的hashcode方法如下面所示。可以看到Date类型的的hash值跟时间有关,两次生成的时间不同,产生的hash值自然也不相同。

public int hashCode() {long ht = this.getTime();return (int) ht ^ (int) (ht >> 32);}

3、Collection中HashMap的应用

平常做开发的时候List和Map作为两种最常见的集合,HashMap作为Map的实现之一,底层的实现采用就是数据+链表的数据结构,即hash得到的值相同的话,采用上述的拉链法来解决冲突。下面就仔细看下HashMap的底层实现:
1、作为一个数组,其默认长度为static final int DEFAULT_INITIAL_CAPACITY = 1 << 4(即16);
2、作为一个hash表,为了避免太多的hash碰撞,于是有了加载因子static final float DEFAULT_LOAD_FACTOR = 0.75f;
3、作为一个数组,在扩容的时候size变为原来的2倍,都是将旧的数据复制到新的数组;
就拿map.put()这个方法作为入口详细扯下(由于HashMap在jdk8引入了红黑树,导致该方法与jdk7实现差异比较大,本文jdk的版本即jdk8):

public V put(K key, V value) {return putVal(hash(key), key, value, false, true);}
 /*** Implements Map.put and related methods** @param hash hash for key* @param key the key* @param value the value to put* @param onlyIfAbsent if true, don't change existing value* @param evict if false, the table is in creation mode.* @return previous value, or null if none*/final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {Node<K,V>[] tab; Node<K,V> p; int n, i;///如果map为空就创建map并返回nullif ((tab = table) == null || (n = tab.length) == 0)n = (tab = resize()).length;/// 如果当前hash得到的数组位置没有存放Node的时候,则就放在这个位置if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);/// 这里就是处理hash碰撞的地方///1、如果当前key与要插入的key相同,则直接覆盖否则2///2、判断p节点是否是TreeNode,也就是判断当前节点链表是否是红黑树,如果是直接在红黑树上插入key-value///3.1、在p节点的链表上遍历到最后一个准备插入,如果找到与要插入的key相同的节点,直接覆盖///3.2、如果当前链表的长度大于7(8-1),将当前链表转换成红黑树并插入节点else {Node<K,V> e; K k;if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;else if (p instanceof TreeNode)e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {p.next = newNode(hash, key, value, null);if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}if (e != null) { // existing mapping for keyV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;afterNodeAccess(e);return oldValue;}}判断是否需要扩容:当前的容量大于capacity*factor(3/4),直接capacity*2++modCount;if (++size > threshold)resize();afterNodeInsertion(evict);return null;}

4、Bloom Filter在海量数据中的应用

Bloom filter即布隆过滤器是一种多hash函数映射的快速查找算法,用于检索一个元素是否在一个集合中。
原理:
利用一个有m位的二进制数组M,这个位数组每一位都初始设为0。假设原始集合S{x1,x2,……xn},Bloom filter使用k个相互独立的hash函数。通过这k个hash函数将原始集合S中的元素都映射到位数组M中,例如将集合S中的x1通过h1,……hk得到x1在位数组M中的k个位置h1(x1)……hk(x1)。
然后将位数组M中的h1(x1)……hk(x1)位置的值设为1,如果之前其它元素已经将该位置设为1了,那么这个位置的值将不做更改还是为1。通过这种方式,将原始集合S中的所有元素都映射到位数组M中。
在查找某个元素x是否属于该集合的时候,通过k个hash函数得到k个位数组M中位置的值。如果这k个位置的值有一个为0,则代表这个元素x不是这个集合中的元素。

换种图像的方式表达上述的意思就是

查找的时候


小结:由查找的过程的可以看出:一种极端情况下:位数组中的所有位都是1的情况下,无论一个元素是否属于这个集合,因为h1(x)……hk(x)得到的都是1,所以x的查找结果就是存在于这个集合中。当然,这个结果是错误。所以推断出bloom filter 的查找结果不是100%准确的。(准备的来说就是:如果一个元素被bloomFilter 判断为不在该集合中,那么这个元素在这个集合中一定不存在。)

一般我们使用bloom filter不用自己来实现了其中的算法,而强大的google为我们使用提供了一个非常便利的包com.google.guava。在这个包里还有其它很强大的例如collect,cache。下面就是采用guava的BloomFilter创建的一个测试demo

public class BloomFilterTest {public static int size = 10000000;public static double fpp = 0.003;public static void main(String[] args) {BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(),size,fpp);for (int i =0;i < size ;++i){bloomFilter.put(i+1);}for (int j =1;j<=size;++j){if (!bloomFilter.mightContain(j)){System.out.println(j+"数据本身在,误判为不在");}}List<Integer> nums = new ArrayList<>();for (int i=1000;size+i<size+2000;++i){if (bloomFilter.mightContain(size+i)) {nums.add(size+i);System.out.println((size+i)+"数据本身不在,误判在");}}System.out.println(nums.size());}}

执行结果

总结:关于hash函数就写到这里,本文只是从一些定义原理,到工作中一些简单的应用和个人的理解做了个小小的总结。如有不对的地方,欢迎指出。

ps:写这个篇博文,停停写写,中间还换了工作,终于下定决心把这个写完,纪念下2018-10-18。

哈希(散列)函数的一些应用相关推荐

  1. java md5 密钥_java加密算法--MD5加密和哈希散列带秘钥加密算法源码

    packagecom.ompa.common.utils;importjava.security.MessageDigest;importjava.security.NoSuchAlgorithmEx ...

  2. 在线字符串哈希/散列加密工具

    在线字符串哈希/散列工具 在线字符串哈希/散列工 本工具可以获取多种散列方式的哈希值,如MD5,SHA1,SHA224,SHA256,SHA384,SHA512,HmacMD5,HmacSHA1等,基 ...

  3. shiro使用md5salt哈希散列加密

    我们先得到MD5加密后的字串 @Testpublic void test01(){//md5Md5Hash md5Hash = new Md5Hash("123456");Syst ...

  4. 03,redis多键值对,哈希散列hset

    // 客户端Jedis连接到服务端,并选择第2个数据库Jedis jedis = new Jedis("127.0.0.1",6379);jedis.select(1);jedis ...

  5. 【算法详解】数据结构:7种哈希散列算法,你知道几个?

    一.前言 哈希表的历史 哈希散列的想法在不同的地方独立出现.1953 年 1 月,汉斯·彼得·卢恩 ( Hans Peter Luhn ) 编写了一份IBM内部备忘录,其中使用了散列和链接.开放寻址后 ...

  6. Hash-哈希/散列

    我们知道,通过对数组进行直接寻址(Direct Addressing),可以在 O(1) 时间内访问数组中的任意元素.所以,如果存储空间允许,可以提供一个数组,为每个可能的关键字保留一个位置,就可以应 ...

  7. Hash(哈希/散列)和Bloom Filter(布隆过滤器)

    文章目录 Hash(函数/表) Bloom Filter 布隆过滤器的误识别问题 总结 参考 Hash(函数/表) Hash (中译为哈希,或者散列)函数在计算机领域,尤其是数据快速查找领域,加密领域 ...

  8. 摘要/哈希/散列算法MD5 SHA1 SHA256 SHA512的区别和MAC算法

    目录 一.摘要算法大致都要经过以下步骤 1. 明文数据预处理 1.1 填充比特 1.2 附加消息长度 2. 摘要计算 2.1 常量初始化 2.2 分组及分组拓展和分组分段 2.3 轮函数(每段一轮,此 ...

  9. 哈希 ---《哈希函数》------除数的选取为什么是质数?、《哈希冲突》------解决方法、《闭散列》、《开散列》

    一.哈希概念 顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较**.顺序查找时间复杂度为O(N),平衡树中为树的高度,即O(logN ) ...

  10. 【C++】哈希——unordered系列容器|哈希冲突|闭散列|开散列

    文章目录 一.unordered系列关联式容器 二.哈希概念 三.哈希冲突 四.哈希函数 五.解决哈希冲突 1.闭散列--开放定址法 2.代码实现 3.开散列--开链法 4.代码实现 六.结语 一.u ...

最新文章

  1. 【译】Why Decentralized AI Matters Part II: Technological Enablers
  2. linux 浏览器 links,linux下的命令行浏览器links
  3. 智能支付稳定性测试实战
  4. jquery获取checkbox是否选中
  5. java应用诊断工具-Cubic v1.3.0
  6. activemq java 重发_java – 无法让ActiveMQ重新发送我的消息
  7. C++的hashmap和Java的hashmap
  8. Cardboard:虚拟现实怎样在国内最容易普及
  9. boost::bind with ros topic,ros中subscribe用boost::bind绑定多个参数
  10. 【李宏毅2020 ML/DL】P118 RL - Model-based, Meta, Multi-agent, Alpha
  11. shell 脚本教程 入门级
  12. 转盘抽奖 canvas 抽奖 H5 源码
  13. Hibernate pojo对象的三种状态
  14. 手机对计算机的远程控制软件,教你用手机远程控制电脑,轻松实现远程操作!...
  15. 北京大学创业训练营专家讲座:创新大师乔布斯的创业理念与营销哲学
  16. 推荐一个 推理屋 网站
  17. 什么是敏捷开发Scrum
  18. Python VUE3 + Django Ninja的后台管理系统
  19. 用74ls90组成二十四进制计数器_一个厉害的芯片芯片74LS190同步计数器可以做加法也可以做减法...
  20. Caliburn.Micro将枚举 绑定到ComboBox

热门文章

  1. 关于C语言中的绝对值函数
  2. 网络计算机amd,AMD多屏显示设置指南_计算机硬件和网络_IT /计算机_信息
  3. 在android客户端加载html源代码总结
  4. 计算机29首流行音乐叫什么,2018结婚用的歌曲排名 50首流行歌曲燃爆婚礼现场...
  5. WIN10下删除Hiberfil.sys文件
  6. 谁在叩响野蛮人的家门?
  7. JS实现网页打印功能
  8. shopNC注册后无法登陆的问题
  9. STM32 BOR/POR/PDR
  10. C/C++可变参数列表参数处理方法va_list、va_start()、va_copy()、va_arg()、va_end()