我在寻找的散列函数:
- 哈希文本字符串以及(如少数碰撞)
- 是用Java编写,并广泛使用
- 奖励:在几个领域工作(而不是我串接他们在连接字符串应用散列)
- 奖励:拥有128位的变体。
- 奖励:不是CPU密集型。
我在寻找的散列函数:
你为什么不使用long
默认的变种String.hashCode()
其中一些很聪明的家伙肯定把精力使其有效-不提的是,已经看了这段代码成千上万开发者的眼睛)?
// adapted from String.hashCode()
public static long hash(String string) {
long h = 1125899906842597L; // prime
int len = string.length();
for (int i = 0; i < len; i++) {
h = 31*h + string.charAt(i);
}
return h;
}
如果你正在寻找更多的位,你很可能使用编辑: BigInteger
正如我在给@brianegge的答案的评论中提到,有没有太大的usecases与超过32位,最有可能不是单一的一个哈希有超过64位的散列:
我能想象分布在几十个服务器,存储也许数百亿映射的一个巨大的哈希表。 对于这样的情况下,@brianegge还在这里有一个正确的观点:32位允许2 ^ 32(约4.3十亿)不同的哈希键。 假设一个强大的算法,你应该还是有比较少的冲突。 随着64位(18446744073个十亿不同的密钥)你肯定保存,无论什么疯狂的情况下,你需要它。 usecases为128个密钥(340,282,366,920,938,463,463,374,607,431十亿可能的密钥)的思考是非常不可能的,但。
要结合哈希几场,根本就异或乘以一个与原并将它们添加:
long hash = MyHash.hash(string1) * 31 + MyHash.hash(string2);
小的素数是在那里,以避免等于哈希代码切换值,即{“富”,“酒吧”}和{“酒吧”,“富”}是不相等的,并应具有不同的散列码。 XOR是糟糕,因为它返回0,如果两个值相等。 因此,{“富”,“富”}和{“酒吧”,“巴”}将具有相同的散列码。
创建SHA-1散列 ,然后屏蔽掉最低的64位。
long hash = string.hashCode();
是的,前32位将是0,但你可能会运行的硬件资源进行运行与哈希冲突的问题了。 在字符串的哈希码是相当有效率和很好的测试。
更新我觉得上面的满足这有可能工作的最简单的事情 ,但是,我同意扩大现有的字符串的hashCode @sfussenegger想法。
除了具有为您的字符串一个很好的hashCode,你可能要考虑在实现换汤不换药的哈希码。 如果你的存储是由其他开发者使用,或与其它类型的使用,这可以帮助分发钥匙。 例如,Java的HashMap的是基于电源的两长度哈希表,所以它增加了这个功能,以确保下位被充分地分布。
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
为什么不使用CRC64多项式。 这些都是合理高效和优化,以确保所有的位进行计数,遍布结果空间。
有很多实现在网络上可用,如果你谷歌“CRC64的Java”
做这样的事情:
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class Test {
public static void main(String[] args) throws NoSuchAlgorithmException,
IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
try {
MessageDigest md = MessageDigest.getInstance("MD5");
SomeObject testObject = new SomeObject();
dos.writeInt(testObject.count);
dos.writeLong(testObject.product);
dos.writeDouble(testObject.stdDev);
dos.writeUTF(testObject.name);
dos.writeChar(testObject.delimiter);
dos.flush();
byte[] hashBytes = md.digest(baos.toByteArray());
BigInteger testObjectHash = new BigInteger(hashBytes);
System.out.println("Hash " + testObjectHash);
} finally {
dos.close();
}
}
private static class SomeObject {
private int count = 200;
private long product = 1235134123l;
private double stdDev = 12343521.456d;
private String name = "Test Name";
private char delimiter = '\n';
}
}
DataOutputStream类让你写的原语和字符串并将它们作为字节输出。 包装一个ByteArrayOutputStream在它会让你写入的字节数组,这与很好地集成消息摘要 。 您可以从列出的任何算法挑选这里 。
最后的BigInteger会让你把输出字节到一个更容易使用的号码。 该MD5和SHA1算法都产生128位的哈希值,所以如果你需要64你可以截断。
SHA1哈希应该几乎所有的东西很好,并与罕见的碰撞(这是128位)。 这个工作从Java,但我不知道它是如何实现的。 它实际上可能是相当快的。 它适用于我在执行几个领域:只是把他们都到DataOutputStream
,你是好去。 你甚至可以反射和注释做(也许@HashComponent(order=1)
显示哪些领域进入一个哈希和以什么顺序)。 它有一个128位的变体,我想你会发现,你觉得会不会使用尽可能多的CPU。
我用这样的代码来获取巨大的数据集的哈希值(由现在大概数十亿个对象),以便能够跨多个后端存储分片他们。 它应适用于任何你需要它。 请注意,我想你可能希望只调用MessageDigest.getInstance()
一次,然后clone()
从此:IIRC克隆是快了很多。
字符串逆向获得另外32位散列代码,然后将二者结合起来:
String s = "astring";
long upper = ( (long) s.hashCode() ) << 32;
long lower = ( (long) s.reverse().hashCode() ) - ( (long) Integer.MIN_VALUE );
long hash64 = upper + lower;
这是伪代码; 在String.reverse()
方法不存在,将需要实施一些其他的方式。
今天的答案(2018)。 SipHash。
这将是比大多数的答案快得多在这里,而且比所有这些品质显著较高。
番石榴库具有一个: https://google.github.io/guava/releases/23.0/api/docs/com/google/common/hash/Hashing.html#sipHash24--
你看看阿帕奇公共朗 ?
但对于64位(128),你需要一些技巧:在书中有效的Java由约书亚布洛赫帮助所设定的规则创建64位散列容易(只使用int的长代替)。 对于128位,你需要额外的黑客...
免责声明:如果您想有效地散列个人自然语言的单词本方案适用。 这是低效散列较长的文本,或含有非字母字符的文本。
我不知道的功能,但这里有一个想法,可能会有所帮助:
然后可以使用剩余的12位来编码字符串的长度(或它的模数值),以进一步减少冲突,或者使用传统的散列函数产生一个12位的散列码。
假设你输入的文字,只有我能想象这会导致极少数碰撞,将是经济的计算(为O(n))。 不像其他的解决方案,到目前为止,这种方法需要域名的问题考虑在内,以减少冲突 -它字谜探测器编程珍珠描述是基于关闭(见这里 )。