线性哈希-line hash

线性哈希是一种动态扩展哈希表的方法。

线性哈希的数学原理:

假定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 实现代码如下:

 

[java]  view plain copy
  1. import java.util.ArrayList;  
  2. import java.util.HashMap;  
  3. import java.util.List;  
  4. import java.util.Map;  
  5.   
  6. public class LineHash {  
  7.       
  8.     public int pageSize;  //桶的容量  
  9.   
  10.     public int overPoint = 0//分裂点  
  11.   
  12.     public int listSize = 4//哈希表的初始大小  
  13.   
  14.     public int initlistSize = 4//哈希大小的记录值  
  15.        
  16.     public int workRound = 1;  //分裂轮数  
  17.       
  18.     public List<Map<Integer, String>> hash = null//模拟哈希表  
  19.   
  20.     public LineHash(int pageSIze) {  
  21.         this.pageSize = pageSIze;  
  22.         hash = new ArrayList<Map<Integer, String>>(4);  
  23.         for (int i = 0; i < listSize; i++) {  
  24.             hash.add(new HashMap<Integer, String>()); //向哈希表中初始化桶  
  25.         }  
  26.     }  
  27.     //查询函数  
  28.     public String getKeyValue(int key){  
  29.         int index = hashFun(key, workRound); //根据分裂轮数调用不同的哈希函数  
  30.         if(index < overPoint){               //当前桶产生了分裂  
  31.             index = hashFun(key, workRound + 1); //采用新的哈希函数进行计算  
  32.         }  
  33.         return hash.get(index).get(key);  
  34.     }  
  35.     //添加函数  
  36.     public void addKeyValue(int key, String value) {   
  37.         int index = hashFun(key, workRound);     
  38.         if(index < overPoint){  
  39.             index = hashFun(key, workRound + 1);  
  40.         }  
  41.         Map<Integer, String> map = hash.get(index);  
  42.         if (map.size() < pageSize) {   //判断当前桶是否满了  
  43.             map.put(key, value);  
  44.         } else {  
  45.             map.put(key, value);    
  46.             splitHash();              //满了就进行分裂  
  47.         }  
  48.     }  
  49.   
  50.     public int hashFun(int key, int f1) {  
  51.         return key % (4 * f1);  
  52.     }  
  53.   
  54.     public void splitHash() {  
  55.         Map<Integer, String> OldMap = hash.get(overPoint);   //旧桶  
  56.         Map<Integer, String> NewMap = new HashMap<Integer, String>(); //分裂产生的新桶  
  57.   
  58.         Integer[] keyList = OldMap.keySet().toArray(new Integer[0]);  
  59.         for (int i = 0; i < keyList.length; i++) {  //准备移动一半的数据到新桶  
  60.             int key = keyList[i].intValue();  
  61.             int index = hashFun(key, workRound + 1);  
  62.             if (index >= listSize) {  
  63.                 String value = OldMap.get(key);  
  64.                 OldMap.remove(key);  
  65.                 NewMap.put(key, value);  
  66.             }  
  67.         }  
  68.         hash.add(NewMap);  //将新桶放入哈希表  
  69.         listSize++;  //哈希表长度增加  
  70.         overPoint++;  //分裂点移动  
  71.         if(overPoint >= initlistSize){  //分裂点移动了一轮就更换新的哈希函数  
  72.             workRound++;  
  73.             initlistSize = initlistSize * 2;  
  74.             overPoint = 0;  
  75.         }  
  76.     }  
  77. }  


测试代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public class testLineHash {  
  2.     public static void main(String args[]){  
  3.         LineHash hash = new LineHash(3);  
  4.         hash.addKeyValue(4"this");  
  5.         hash.addKeyValue(8"is");  
  6.         hash.addKeyValue(12"a");  
  7.         hash.addKeyValue(16"test");  
  8.         hash.addKeyValue(20"!!!!");  
  9.         hash.addKeyValue(24"~~~");  
  10.         hash.addKeyValue(28"dsd");  
  11.         hash.addKeyValue(32"gg22");  
  12.           
  13.         for(int i = 4; i <= 24;)  
  14.         {  
  15.             System.out.println(hash.getKeyValue(i));  
  16.             i = i + 4;  
  17.         }  
  18.     }  
  19. }  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值