Java 将哈希值映射到一个范围,冲突最少 上下文
嗨,我正在为学校做一个作业,要求我们用Java实现一个哈希表。没有将碰撞保持在最低限度的要求,但在我所做的所有工作中,低碰撞率和低碰撞速度似乎是两个最受欢迎的品质 问题 我想要一些关于如何将散列函数的输出映射到一个较小范围的指导,而不让>20%的键发生冲突(yikes) 在我所研究的所有算法中,键都映射到无符号32位整数的整个范围(或者在许多情况下是64位,甚至128位)。我在这里、维基百科或我遇到的任何与哈希相关的文章/讨论中都找不到太多关于这方面的内容 就我实现的细节而言,我使用Java(我学校的授权),这是有问题的,因为没有未签名的类型可以使用。为了解决这个问题,我一直在使用64位长的整数类型,然后使用位掩码映射回32位。我不是简单地截断,而是将最上面的32位与最下面的32位进行异或运算,然后执行逐位AND运算,以屏蔽掉当我将其向下转换为32位整数时可能导致负值的任何高位。在所有这些之后,一个单独的函数将结果哈希值向下压缩,以适应哈希表内部数组的边界 它最终看起来像:Java 将哈希值映射到一个范围,冲突最少 上下文,java,hash,hashtable,Java,Hash,Hashtable,嗨,我正在为学校做一个作业,要求我们用Java实现一个哈希表。没有将碰撞保持在最低限度的要求,但在我所做的所有工作中,低碰撞率和低碰撞速度似乎是两个最受欢迎的品质 问题 我想要一些关于如何将散列函数的输出映射到一个较小范围的指导,而不让>20%的键发生冲突(yikes) 在我所研究的所有算法中,键都映射到无符号32位整数的整个范围(或者在许多情况下是64位,甚至128位)。我在这里、维基百科或我遇到的任何与哈希相关的文章/讨论中都找不到太多关于这方面的内容 就我实现的细节而言,我使用Java(我
int hash( String key ) {
long h;
for( int i = 0; i < key.length(); i++ )
//do some stuff with each character in the key
h = h ^ ( h << 32 );
return h & 2147483647;
}
其中p是小于内部数组大小的最大素数
这是一种公认的压缩哈希值的方法吗?我有一种感觉,它不是,但由于在压缩之前性能就很差,我也有一种感觉,它不是主要的罪魁祸首。我不知道我是否理解您的具体问题,但我将尝试在哈希性能和冲突方面提供帮助 基于散列的对象将根据散列值确定它们将在哪个bucket中存储键值对。在每个bucket中都有一个存储对的结构(在HashMap中是LinkedList) 如果散列值通常相同,则bucket通常相同,因此性能会降低很多,让我们看一个示例: 以这门课为例
package hashTest;
import java.util.Hashtable;
public class HashTest {
public static void main (String[] args) {
Hashtable<MyKey, String> hm = new Hashtable<>();
long ini = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
MyKey a = new HashTest().new MyKey(String.valueOf(i));
hm.put(a, String.valueOf(i));
}
System.out.println(hm.size());
long fin = System.currentTimeMillis();
System.out.println("tiempo: " + (fin-ini) + " mls");
}
private class MyKey {
private String str;
public MyKey(String i) {
str = i;
}
public String getStr() {
return str;
}
@Override
public int hashCode() {
return 0;
}
@Override
public boolean equals(Object o) {
if (o instanceof MyKey) {
MyKey aux = (MyKey) o;
if (this.str.equals(aux.getStr())) {
return true;
}
}
return false;
}
}
}
性能非常差,现在我们要更改MyKey哈希代码:
package hashTest;
import java.util.Hashtable;
public class HashTest {
public static void main (String[] args) {
Hashtable<MyKey, String> hm = new Hashtable<>();
long ini = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
MyKey a = new HashTest().new MyKey(String.valueOf(i));
hm.put(a, String.valueOf(i));
}
System.out.println(hm.size());
long fin = System.currentTimeMillis();
System.out.println("tiempo: " + (fin-ini) + " mls");
}
private class MyKey {
private String str;
public MyKey(String i) {
str = i;
}
public String getStr() {
return str;
}
@Override
public int hashCode() {
return str.hashCode() * 31;
}
@Override
public boolean equals(Object o) {
if (o instanceof MyKey) {
MyKey aux = (MyKey) o;
if (this.str.equals(aux.getStr())) {
return true;
}
}
return false;
}
}
}
有一个令人难以置信的更好的表现,现在有一个小的变化。返回哈希代码乘以素数(在本例中为31)是一种非常常见的做法,使用与equals方法中相同的哈希代码成员来确定两个对象是否相同(在本例中为str)
我希望这个小例子能为你的问题指出解决办法 您希望将440K值填充到多大的数组中?大于20%的键冲突有什么问题?你感兴趣的不是把很多东西放在一个单独的存储桶中,也不是让散列变得独一无二。我用一个数组来衡量性能,这个数组的大小是我需要散列的元素数量的两倍。我的教授说1%或低至100次冲突是一个理想的比率。因为Java 7,你可以使用
Objects.hash(Object…values)
快速高效地创建一个散列。显然,我并不期望达到或超过本机进行的散列。这是大学的作业,我们必须从头做起。
100000
tiempo: 62866 mls
package hashTest;
import java.util.Hashtable;
public class HashTest {
public static void main (String[] args) {
Hashtable<MyKey, String> hm = new Hashtable<>();
long ini = System.currentTimeMillis();
for (int i=0; i<100000; i++) {
MyKey a = new HashTest().new MyKey(String.valueOf(i));
hm.put(a, String.valueOf(i));
}
System.out.println(hm.size());
long fin = System.currentTimeMillis();
System.out.println("tiempo: " + (fin-ini) + " mls");
}
private class MyKey {
private String str;
public MyKey(String i) {
str = i;
}
public String getStr() {
return str;
}
@Override
public int hashCode() {
return str.hashCode() * 31;
}
@Override
public boolean equals(Object o) {
if (o instanceof MyKey) {
MyKey aux = (MyKey) o;
if (this.str.equals(aux.getStr())) {
return true;
}
}
return false;
}
}
}
100000
tiempo: 47 mls