本地文本索引及java的HashMap实现

来源:互联网 发布:汽车改装模拟软件 编辑:程序博客网 时间:2024/06/05 02:59

这个学期的信息检索课要做一个大的作业,其实就是相当于一个搜索引擎,但是是基于本地文本文件的检索,不需要扒网页,也不能使用Lucence或Lemur等强大的第三方开源软件,要全部自己完成。

要说索引倒是十分简单,就是倒排而已,并没有什么复杂,而且第一版的作业要求只要以字为索引即可,而且不需要对结果进行排序和高亮显示,甚至不需要记录该字在文档中出现的次数和位置。所以在完成的时候也就是使用了一个较为简单的存储数据结构,即HashMap<string, HashSet<int>>这样一个结构,第一个参数存储的是字,后面的HashSet中存储的是该字出现的文档号,由于字和文档号都不需要重复记录,所以采用了哈希结构。

不久又开始做数据库的作业,内容是基于数据库的信息检索,这次的数据量相对要大很多,大概有300多M的数据,而且必须用C++来完成,开始是用STL的map来做,但是map是没有实现哈希的,插入时要判断重复,所以速度会指数级变慢,380万条数据处理到50万时就已经慢到不行了,而且吃内存十分严重,所以就希望自己实现一个HashMap。

虽然网上也有现成的代码,但是还是想自己写一个,想到java中用到了JDK1.6的HashMap,就想着顺便看一下JDK的源码,然后照猫画虎移植到C++中,大概花了一天左右的时间实现了一个较简单的,没有实现迭代器的HashMap,顺便记录一下在看JDK源码时的一些收获,毕竟写得还是相当精彩的,可以这么说吧。

首先是散列码的计算,这是做哈希最重要的一部分,没有一个好的散列函数,一切都是空谈,下面是JDK1.6中哈希的计算,一些参数我直接初始化使用了,源码中不是这样写的,但是结果是一样的,这里是String类hashCode的计算:

上面是String类的hashCode的计算,在HashMap中,基于这个结果又进行了进一步计算,算法如下:

其中参数h就是String的hashCode,虽然是简单的四次移位和六次按位异或的计算,但是效果却是惊人的,经测试,在使用0.75作为负载因子,380万条数据共1万多个键的分布情况是极为平均的,并且移位和按位逻辑运算是在所有运算中最快的,即使处理的都是中文效果也非常好,比起网上给的一些针对GBK编码中文的哈希函数的运算速度要快很多,并且在求下标位置时,采用的也是位与运算,而非求模,可以说在性能上真是精益求精啊。

当然这个散列函数并不能保证不会出现冲突,而出现冲突的处理方式也很简单,就是使用了类似链表的结构(后面所说的都是使用C++的描述方式),哈希值相同的节点被存在同一个table中,而所谓table就是一个Entry链表的入口,Entry中存储了键值对以及另一个Entry的指针,其实java中实现时要求在总的Entry数大于table数乘以负载因子时,就要resize,也就是要将table扩大一倍,这样的话基本可以满足一个table内只有一个Entry,所以效率还是可以的,但是对于内存的使用还是相当厉害的。还有就是在table中插入Entry时为了保证不需要Entry的遍历,直接将新的Entry插在table的头部,而将之前的链表接在新Entry的后面,速度也快很多,这是网上一些实现HashMap的人并没有注意的地方,由于在C++中使用的都是指针,无论resize还是遍历,速度都很快,最后的效果也不错。

大概就这么多,最后顺便看了一下JDK中HashSet的实现,发现其实就是一个HashMap,只不过将第二个参数随便new了一个Object加了进去,所有的方法全部直接使用了HashMap的方法,真的是被骗了的感觉啊……

原创粉丝点击