在学习 HashMap 前,咱们先来温习下 Hash(哈希) 的概念。html
Hash(哈希),又称“散列”。java
散列(hash)英文原意是“混杂”、“拼凑”、“从新表述”的意思。web
在某种程度上,散列是与排序相反的一种操做,排序是将集合中的元素按照某种方式好比字典顺序排列在一块儿,而散列经过计算哈希值,打破元素之间原有的关系,使集合中的元素按照散列函数的分类进行排列。算法
在介绍一些集合时,咱们总强调须要重写某个类的 equlas() 方法和 hashCode() 方法,确保惟一性。这里的 hashCode() 表示的是对当前对象的惟一标示。计算 hashCode 的过程就称做 哈希。数组
咱们一般使用数组或者链表来存储元素,一旦存储的内容数量特别多,须要占用很大的空间,并且在查找某个元素是否存在的过程当中,数组和链表都须要挨个循环比较,而经过 哈希 计算,能够大大减小比较次数。缓存
如今有 4 个数 {2,5,9,13},须要查找 13 是否存在。服务器
int[] numbers = new int[]{2,5,9,13}; for (int i = 0; i < numbers.length; i++) { if (numbers[i] == 13){ System.out.println("find it!"); return; } }
这样须要遍历 4 次才能找到,时间复杂度为 O(n)。markdown
H[key] = key % 3;
四个数 {2,5,9,13} 对应的哈希值为:网络
H[2] = 2 % 3 = 2; H[5] = 5 % 3 = 2; H[9] = 9 % 3 = 0; H[13] = 13 % 3 = 1;
而后把它们存储到对应的位置。数据结构
当要查找 13 时,只要先使用哈希函数计算它的位置,而后去那个位置查看是否存在就行了,本例中只需查找一次,时间复杂度为 O(1)。
好比你和我同样是个剁手族买书狂,家里书一大堆,若是书存放时不分类直接摆到书架上(数组存储),找某本书时可能须要脑壳从左往右从上往下转好几圈才能发现;若是存放时按照类别分开放,技术书、小说、文学等等分开(按照某种哈希函数计算),找书时只要从它对应的分类里找,天然省事多了。
哈希的过程当中须要使用哈希函数进行计算。
哈希函数是一种映射关系,根据数据的关键词 key ,经过必定的函数关系,计算出该元素存储位置的函数。
表示为:
address = H [key]
构造哈希函数的方法不少,实际工做中要根据不一样的状况选择合适的方法,总的原则是尽量少的产生冲突。
一般考虑的因素有关键字的长度和分布状况、哈希值的范围等。
如:当关键字是整数类型时就能够用除留余数法;若是关键字是小数类型,选择随机数法会比较好。
选用哈希函数计算哈希值时,可能不一样的 key 会获得相同的结果,一个地址怎么存放多个数据呢?这就是冲突。
经常使用的主要有两种方法解决冲突:
1.连接法(拉链法)
拉链法解决冲突的作法是:
将全部关键字为同义词的结点连接在同一个单链表中。
若选定的散列表长度为 m,则可将散列表定义为一个由 m 个头指针组成的指针数组 T[0..m-1] 。
凡是散列地址为 i 的结点,均插入到以 T[i] 为头指针的单链表中。
T 中各份量的初值均应为空指针。
在拉链法中,装填因子 α 能够大于 1,但通常均取 α ≤ 1。
2.开放定址法
用开放定址法解决冲突的作法是:
用开放定址法解决冲突的作法是:当冲突发生时,使用某种探测技术在散列表中造成一个探测序列。沿此序列逐个单元地查找,直到找到给定的关键字,或者碰到一个开放的地址(即该地址单元为空)为止(若要插入,在探查到开放的地址,则可将待插入的新结点存人该地址单元)。查找时探测到开放的地址则代表表中无待查的关键字,即查找失败。
简单的说:当冲突发生时,使用某种探查(亦称探测)技术在散列表中寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到。
按照造成探查序列的方法不一样,可将开放定址法区分为线性探查法、二次探查法、双重散列法等。
a.线性探查法
hi=(h(key)+i) % m ,0 ≤ i ≤ m-1
基本思想是:
探查时从地址 d 开始,首先探查 T[d],而后依次探查 T[d+1],…,直到 T[m-1],此后又循环到 T[0],T[1],…,直到探查到 有空余地址 或者到 T[d-1]为止。
b.二次探查法
hi=(h(key)+i*i) % m,0 ≤ i ≤ m-1
基本思想是:
探查时从地址 d 开始,首先探查 T[d],而后依次探查 T[d+1^2],T[d+2^2],T[d+3^2],…,等,直到探查到 有空余地址 或者到 T[d-1]为止。
缺点是没法探查到整个散列空间。
c.双重散列法
hi=(h(key)+i*h1(key)) % m,0 ≤ i ≤ m-1
基本思想是:
探查时从地址 d 开始,首先探查 T[d],而后依次探查 T[d+h1(d)], T[d + 2*h1(d)],…,等。
该方法使用了两个散列函数 h(key) 和 h1(key),故也称为双散列函数探查法。
定义 h1(key) 的方法较多,但不管采用什么方法定义,都必须使 h1(key) 的值和 m 互素,才能使发生冲突的同义词地址均匀地分布在整个表中,不然可能形成同义词地址的循环计算。
该方法是开放定址法中最好的方法之一。
哈希表(hash table)是哈希函数最主要的应用。
哈希表是实现关联数组(associative array)的一种数据结构,普遍应用于实现数据的快速查找。
用哈希函数计算关键字的哈希值(hash value),经过哈希值这个索引就能够找到关键字的存储位置,即桶(bucket)。哈希表不一样于二叉树、栈、序列的数据结构通常状况下,在哈希表上的插入、查找、删除等操做的时间复杂度是 O(1)。
查找过程当中,关键字的比较次数,取决于产生冲突的多少,产生的冲突少,查找效率就高,产生的冲突多,查找效率就低。所以,影响产生冲突多少的因素,也就是影响查找效率的因素。
影响产生冲突多少有如下三个因素:
哈希表的加载因子和容量决定了在何时桶数(存储位置)不够,须要从新哈希。
加载因子太大的话桶太多,遍历时效率变低;太大的话频繁 rehash,致使性能下降。因此加载因子的大小须要结合时间和空间效率考虑。
在 HashMap 中的加载因子为 0.75,即四分之三。
网络环境下的分布式缓存系统通常基于一致性哈希(Consistent hashing)。简单的说,一致性哈希将哈希值取值空间组织成一个虚拟的环,各个服务器与数据关键字K使用相同的哈希函数映射到这个环上,数据会存储在它顺时针“游走”遇到的第一个服务器。可使每一个服务器节点的负载相对均衡,很大程度上避免资源的浪费。
在动态分布式缓存系统中,哈希算法的设计是关键点。使用分布更合理的算法可使得多个服务节点间的负载相对均衡,能够很大程度上避免资源的浪费以及部分服务器过载。 使用带虚拟节点的一致性哈希算法,能够有效地下降服务硬件环境变化带来的数据迁移代价和风险,从而使分布式缓存系统更加高效稳定。
http://www.nowamagic.net/librarys/veda/detail/1273
http://blog.csdn.net/cywosp/article/details/23397179/
http://www.cnblogs.com/qiaoshanzi/p/5295554.html
http://baike.baidu.com/view/549615.htm
http://sjjp.tjuci.edu.cn/sjjg/DataStructure/DS/web/chazhao/chazhao9.4.3.3.htm