(http://youzitool.com 新博客,欢迎访问)
线性哈希是一种动态扩展哈希表的方法。
线性哈希的数学原理:
假定key = 5 、 9 、13
key % 4 = 1
现在我们对8求余
5 % 8 = 5
9 % 8=1
13 % 8 = 5
由上面的规律可以得出
(任意key) % n = M
(任意key) %2n = M或 (任意key) %2n = M + n
线性哈希的具体实现:
我们假设初始化的哈希表如下:
分裂点 | 桶编号 | 桶中已存储的Key | 溢出key |
* | 0 | 4,8,12 |
|
| 1 | 5,9 |
|
| 2 | 6 |
|
| 3 | 7,11,15,19, 23 |
|
Figure1
为了方便叙述,我们作出以下假定:
1:为了使哈希表能进行动态的分裂,我们从桶0开始设定一个分裂点。
2:一个桶的容量为listSize = 5,当桶的容量超出后就从分裂点开始进行分裂。
3:hash函数为 h0 = key %4 h1 = key % 8,h1会在分裂时使用。
4:整个表初始化包含了4个桶,桶号为0-3,并已提前插入了部分的数据。
分裂过程如下:
现在插入key = 27
1:进行哈希运算,h0 = 27 % 4 = 3
2:将key = 27插入桶3,但发现桶3已经达到了桶的容量,所以触发哈希分裂
3:由于现在分裂点处于0桶,所以我们对0桶进行分割。这里需要注意虽然这里是3桶满了,但我们并不会直接从3桶进行分割,而是从分割点进行分割。这里为什么这么做,在下面会进一步介绍。
4:对分割点所指向的桶(桶0)所包含的key采用新的hash函数(h1)进行分割。
对所有key进行新哈希函数运算后,将产生如下的哈希表
分裂点 | 桶编号 | 桶中已存储的Key | 溢出key |
| 0 | 8 |
|
* | 1 | 5,9 |
|
| 2 | 6 |
|
| 3 | 7,11,15,19, 23 | 27 |
| 4 | 4,12 |
|
Figure2
5:虽然进行了分裂,但桶3并不是分裂点,所以桶3会将多出的key,放于溢出页.,一直等到桶3进行分裂。
6:进行分裂后,将分裂点向后移动一位。
一次完整的分裂结束。
key的读取:
采用h0对key进行计算。
如果算出的桶号小于了分裂点,表示桶已经进行的分裂,我们采用h1进行hash运算,算出key所对应的真正的桶号。再从真正的桶里取出value
如果算出的桶号大于了分裂点,那么表示此桶还没进行分裂,直接从当前桶进行读取value。
说明:
1:如果下一次key插入0、1、2、4桶,是不会触发分裂。(没有超出桶的容量)如果是插入桶3,用户在实现时可以自己设定,可以一旦插入就触发,也可以等溢出页达到listSize再触发新的分裂。
2:现在0桶被分裂了,新数据的插入怎么才能保证没分裂的桶能正常工作,已经分裂的桶能将部分插入到新分裂的桶呢?
只要分裂点小于桶的总数,我们依然采用h0函数进行哈希计算。
如果哈希结果小于分裂号,那么表示这个key所插入的桶已经进行了分割,那么我就采用h1再次进行哈希,而h1的哈希结果就这个key所该插入的桶号。
如果哈希结果大于分裂号,那么表示这个key所插入的桶还没有进行分裂。直接插入。
这也是为什么虽然是桶3的容量不足,但分裂的桶是分裂点所指向的桶。如果直接在桶3进行分裂,那么当新的key插入的时候就不能正常的判断哪些桶已经进行了分裂。
3:如果使用分割点,就具备了无限扩展的能力。当分割点移动到最后一个桶(桶3)。再出现分裂。那么分割点就会回到桶0,到这个时候,h0作废,h1替代h0, h2(key % 12)替代h1。那么又可以开始动态分割。那个整个初始化状态就发生了变化。就好像没有发生过分裂。那么上面的规则就可以循环使用。
3:线性哈希的论文中是按上面的规则来进行分裂的。其实我们可以安装自己的实际情况来进行改动。
假如我们现在希望去掉分割点,一旦哪个桶满了,马上对这个桶进行分割。
可以考虑了以下方案:
1:为所有桶增加一个标志位。初始化的时候对所有桶的标志位清空。
2:一旦某个桶满了,直接对这个桶进行分割,然后将设置标志位。当新的数据插入的时候,经过哈希计算(h0)发现这个桶已经分裂了,那么就采用新的哈希函数(h1)来计算分裂之后的桶号。在读取数据的时候处理类似。
Linehash 实现代码如下:
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class LineHash {
public int pageSize; //桶的容量
public int overPoint = 0; //分裂点
public int listSize = 4; //哈希表的初始大小
public int initlistSize = 4; //哈希大小的记录值
public int workRound = 1; //分裂轮数
public List<Map<Integer, String>> hash = null; //模拟哈希表
public LineHash(int pageSIze) {
this.pageSize = pageSIze;
hash = new ArrayList<Map<Integer, String>>(4);
for (int i = 0; i < listSize; i++) {
hash.add(new HashMap<Integer, String>()); //向哈希表中初始化桶
}
}
//查询函数
public String getKeyValue(int key){
int index = hashFun(key, workRound); //根据分裂轮数调用不同的哈希函数
if(index < overPoint){ //当前桶产生了分裂
index = hashFun(key, workRound + 1); //采用新的哈希函数进行计算
}
return hash.get(index).get(key);
}
//添加函数
public void addKeyValue(int key, String value) {
int index = hashFun(key, workRound);
if(index < overPoint){
index = hashFun(key, workRound + 1);
}
Map<Integer, String> map = hash.get(index);
if (map.size() < pageSize) { //判断当前桶是否满了
map.put(key, value);
} else {
map.put(key, value);
splitHash(); //满了就进行分裂
}
}
public int hashFun(int key, int f1) {
return key % (4 * f1);
}
public void splitHash() {
Map<Integer, String> OldMap = hash.get(overPoint); //旧桶
Map<Integer, String> NewMap = new HashMap<Integer, String>(); //分裂产生的新桶
Integer[] keyList = OldMap.keySet().toArray(new Integer[0]);
for (int i = 0; i < keyList.length; i++) { //准备移动一半的数据到新桶
int key = keyList[i].intValue();
int index = hashFun(key, workRound + 1);
if (index >= listSize) {
String value = OldMap.get(key);
OldMap.remove(key);
NewMap.put(key, value);
}
}
hash.add(NewMap); //将新桶放入哈希表
listSize++; //哈希表长度增加
overPoint++; //分裂点移动
if(overPoint >= initlistSize){ //分裂点移动了一轮就更换新的哈希函数
workRound++;
initlistSize = initlistSize * 2;
overPoint = 0;
}
}
}
测试代码:
public class testLineHash {
public static void main(String args[]){
LineHash hash = new LineHash(3);
hash.addKeyValue(4, "this");
hash.addKeyValue(8, "is");
hash.addKeyValue(12, "a");
hash.addKeyValue(16, "test");
hash.addKeyValue(20, "!!!!");
hash.addKeyValue(24, "~~~");
hash.addKeyValue(28, "dsd");
hash.addKeyValue(32, "gg22");
for(int i = 4; i <= 24;)
{
System.out.println(hash.getKeyValue(i));
i = i + 4;
}
}
}
本文只是我的一点小看法,如果有什么不对的地方希望大家指出。在下不胜感激~