Time33哈希算法

来源:互联网 发布:网络的弊端英语作文 编辑:程序博客网 时间:2024/06/06 15:01

原文:http://blog.csdn.net/chen_alvin/article/details/5846714

接到任务,需要编程求两个十万级记录列表的交集。分析后,有两个解决思路:一、在数据库中建立临时表,导入数据后,用SQL语句才交集,再导出数据;二、直接内存运算求解。考虑到数据记录多,数据库运算压力大,最后决定选择第二个方法。

 

涉及到数据查找比对,首先考虑到使用HashSet。HashSet最大的好处就是实现查找时间复杂度为O(1)。使用HashSet需要解决一个重要问题:冲突问题。对比研究了网上一些字符串哈希函数,发现几乎所有的流行的HashMap都采用了DJB Hash Function,俗称“Times33”算法。Times33的算法很简单,就是不断的乘33,见下面算法原型。

view plain
  1. hash(i) = hash(i-1) * 33 + str[i]  

 

Time33在效率和随机性两方面上俱佳。对于一个Hash函数,评价其优劣的标准应为随机性,即对任意一组标本,进入Hash表每一个单元(cell)之概率的平均程度,因为这个概率越平均,数据在表中的分布就越平均,表的空间利用率就越高。

 

view plain
  1. int Time33(String str) {  
  2.     int len = str.length();  
  3.     int hash = 0;  
  4.     for (int i = 0; i < len; i++)  
  5.         // (hash << 5) + hash 相当于 hash * 33  
  6.         hash = (hash << 5) + hash + (int) str.charAt(i);  
  7.     return hash;  
  8. }  

 

Java的HashSet判断冲突,主要依靠对象的HashCode和equals方法。因此对象必须重写hashCode和equals两个方法。

以下测试构造300,0000个Item对象,放入到HashSet中,测试Time33算法。

 

view plain
  1. /** 
  2.  * Item类 
  3.  * 重写hashCode和equals方法 
  4.  */  
  5. class Item {  
  6.     public Item(String[] strs) {  
  7.         columns = strs;  
  8.     }  
  9.   
  10.     public boolean equals(Object otherObject) {  
  11.         if (this == otherObject)  
  12.             return true;  
  13.         if (otherObject == null)  
  14.             return false;  
  15.         if (getClass() != otherObject.getClass())  
  16.             return false;  
  17.         if (!(otherObject instanceof Item))  
  18.             return false;  
  19.         Item other = (Item) otherObject;  
  20.         if (this.columns.length != other.columns.length)  
  21.             return false;  
  22.         for (int i = 0; i < this.columns.length; i++) {  
  23.             if (this.columns[i] != null && !this.columns[i].equals(other.columns[i]))  
  24.                 return false;  
  25.         }  
  26.         return true;  
  27.     }  
  28.   
  29.     public int hashCode() {  
  30.         StringBuffer sb = new StringBuffer();  
  31.         for (int i = 0; i < this.columns.length; i++) {  
  32.             sb.append(this.columns[i]);  
  33.         }  
  34.         return this.Time33(sb.toString());  
  35.     }  
  36.   
  37.     private int Time33(String str) {  
  38.         int len = str.length();  
  39.         int hash = 0;  
  40.         for (int i = 0; i < len; i++)  
  41.             hash = (hash << 5) + hash + (int) str.charAt(i);  
  42.         return hash;  
  43.     }  
  44.   
  45.     private String[] columns;  
  46. }  

 

view plain
  1. HashSet<Item> hashSet = new HashSet<Item>();  
  2. long start;  
  3. long end;  
  4. String[] strs = { "Alvin""Chan""HashSet""WOW" };  
  5. String[] tmp;  
  6. int count = 0;  
  7.       
  8. start = System.currentTimeMillis();  
  9.   
  10. for (int i = 0; i < 3000000; i++) {  
  11.     tmp = new String[4];  
  12.     for (int j = 0; j < 4; j++) {  
  13.         // 用strs中随机抽取字符串,并加入随机数,生成tmp字符串数组  
  14.         tmp[j] = strs[(int) (Math.random() * 3)] + (int) (Math.random() * i);  
  15.     }  
  16.     // 加入无法插入到hashSet中,计数器加1  
  17.     if(!hashSet.add(new Item(tmp))) {  
  18.         count++;  
  19.     }  
  20. }  
  21.   
  22. end = System.currentTimeMillis();  
  23.   
  24. System.out.println("插入300,0000条记录");  
  25. System.out.println("所需时间:" + (end - start) + " ms");  
  26. System.out.println("插入个数:" + hashSet.size());  
  27. System.out.println("失败次数:" + count);  

 

测试运行了5次,结果如下:(数据量较大,建议加大JVM的内存再运行测试,否则会内存溢出)

view plain
  1. 1次  
  2. 插入300,0000条记录  
  3. 所需时间:34203 ms  
  4. 插入个数:3000000  
  5. 失败次数:0  
  6.   
  7. 2次  
  8. 插入300,0000条记录  
  9. 所需时间:33063 ms  
  10. 插入个数:3000000  
  11. 失败次数:0  
  12.   
  13. 3次  
  14. 插入300,0000条记录  
  15. 所需时间:33016 ms  
  16. 插入个数:3000000  
  17. 失败次数:0  
  18.   
  19. 4次  
  20. 插入300,0000条记录  
  21. 所需时间:33062 ms  
  22. 插入个数:3000000  
  23. 失败次数:0  
  24.   
  25. 5次  
  26. 插入300,0000条记录  
  27. 所需时间:33140 ms  
  28. 插入个数:3000000  
  29. 失败次数:0  

 

从测试结果来看,面对百万级条记录,使用了Time33作为哈希函数的HashSet能较好地解决冲突问题。


原创粉丝点击