略谈Hash
这几天自己写了个hash表,以前都是用的系统的,现在轮到自己写了,写的还是比较菜的,希望自己继续扩充吧,下面就简单介绍一下。
一、引文
先分析一下最基本的两种数据结构:数组和链表
优缺点分析:
数据结构 | 数据查找 | 数据增删 |
数组 | 数据储存地址是连续,对于查找数据时可以通过数组下标很快定位 | 需要重新分配空间,所耗时间较多 |
链表 | 数据之间只是通过一个地址在连接,查找数据时需要遍历许多不必要的数据 | 由于本身数据之间的连接是通过地址的指向,所以只需要改变一下指向 |
由上可以看出,以上两种数据结构在数据上的查找与增删都有自己的优缺点,而hash结构就是综合了两者的优点。
二、数据结构——Hash表
1).图示结构
2). 从上面的结构图可以看出,hash表整体是以数组为载体,数组内部元素以链表形式存在的,hash表所需要的就是把所要储存的元素平均分配到各个挂表上去,此时所需要的就是hash函数了,我所实现的哈希表是对字符串形式数据的操作,常用字符串哈希函数有BKDRHash,APHash,DJBHash,JSHash,RSHash,SDBMHash,另外还有ELFHash,APHash等等,都是十分简单有效的方法。这些函数使用位运算使得每一个字符都对最后的函数值产生影响。另外还有以MD5和SHA1为代表的杂凑函数,这些函数几乎不可能找到碰撞。
算是站在巨人的肩膀上,我基本写出了一个像样的hash表。
以上常用的几个hash函数代码实现及比较,我已经上传到下面的附件,大家可以看一下。
三、代码示例
1)一些所需要的数据变量:
private int hash_length = 0;// 数组长度
private int threatHold = 0;// 重新加载的条件
private DataNode dataNodes[];// 数据表
private double load_factor = 0.8f;// 加载因子
private int size = 0;// 数据的数目
private static int default_hash_size = 10;// 数组初始大小
private int MAXIMUM_NODE = 1 << 30;// 数据最大值
2)数据的插入及删除
// 以数据单项插入
public synchronized void insert(String data) {
int hash = hash(data);
DataNode node = new DataNode(data);
if (dataNodes[hash] == null)
dataNodes[hash] = node;
else {
DataNode fatherNode = dataNodes[hash];
DataNode childNode = dataNodes[hash].getNextNode();
while (childNode != null) {
fatherNode = childNode;
childNode = childNode.getNextNode();
}
childNode = node;
fatherNode.setNextNode(childNode);
}
if (size++ > threatHold) {
rehash();
}
if (size > MAXIMUM_NODE) {
throw new RuntimeException("Sorry,散列表已满!!!");
}
}
// 以数据形式删除数据项
public synchronized void delete(String data) {
int hash = hash(data);
DataNode node = new DataNode(data);
if (dataNodes[hash] == null) {
throw new RuntimeException("该数据项不存在");
} else if (dataNodes[hash].equals(node)) {
dataNodes[hash] = dataNodes[hash].getNextNode();
} else {
DataNode rootNode = dataNodes[hash].getNextNode();
while (rootNode != node) {
if (rootNode == null)
throw new RuntimeException("该数据项不存在");
rootNode = rootNode.getNextNode();
}
}
size--;
}
3)我借用了一下前人的RS hash函数,系统是通过每个对象的hashcode进行操作
// hash函数用来计算数据的key值
public int hash(String data) {
//RS hash
char[] datas = data.trim().toCharArray();
int temp1 = 378551;
int temp2 = 63689;
int hash = 0;
for(char ch:datas){
hash = hash * temp2 + ch;
temp2 *= temp1;
}
return (hash & 0x7FFFFFFF)%hash_length;
}
4)比较重要的一点,当hash表数据量达到了开始设定的边界,便需要再次加载,称之为rehash
// 重新装载
public synchronized void rehash() {
System.out.println("又要重新加载了.......");
hash_length = hash_length << 1;// 扩充为原来的两倍
if (hash_length > MAXIMUM_NODE) {
throw new RuntimeException("对不起,数组长度已达到最大!!");
}
DataNode newNodes[] = new DataNode[hash_length];
for (DataNode node : dataNodes) {
while (node != null) {
String data = node.getData();
int hash = hash(data);
DataNode nodeNew = new DataNode(data);
if (newNodes[hash] == null)
newNodes[hash] = nodeNew;
else {
DataNode fatherNode = newNodes[hash];
DataNode childNode = newNodes[hash].getNextNode();
while (childNode != null) {
fatherNode = childNode;
childNode = fatherNode.getNextNode();
}
childNode = nodeNew;
fatherNode.setNextNode(childNode);
}
node = node.getNextNode();
}
}
// 复制原来的数据
dataNodes = newNodes;
setThreatHold();
}
四.后文
这实现的基本上只有一个架构,对于很多的细节方面考虑还比较少,接下来主要考虑的还是数据的均分问题,估计更多的还是需要用到位运算,还需要继续下去....