什么是Java中的一个很好的64位散列函数的文本字符串?(What is a good 64bit

2019-09-03 23:57发布

我在寻找的散列函数:

  1. 哈希文本字符串以及(如少数碰撞)
  2. 是用Java编写,并广泛使用
  3. 奖励:在几个领域工作(而不是我串接他们在连接字符串应用散列)
  4. 奖励:拥有128位的变体。
  5. 奖励:不是CPU密集型。

Answer 1:

你为什么不使用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,如果两个值相等。 因此,{“富”,“富”}和{“酒吧”,“巴”}将具有相同的散列码。



Answer 2:

创建SHA-1散列 ,然后屏蔽掉最低的64位。



Answer 3:

long hash = string.hashCode();

是的,前32位将是0,但你可能会运行的硬件资源进行运行与哈希冲突的问题了。 在字符串的哈希码是相当有效率和很好的测试。

更新我觉得上面的满足这有可能工作最简单的事情 ,但是,我同意扩大现有的字符串的hashCode @sfussenegger想法。

除了具有为您的字符串一个很好的hashCode,你可能要考虑在实现换汤不换药的哈希码。 如果你的存储是由其他开发者使用,或与其它类型的使用,这可以帮助分发钥匙。 例如,Java的HashMap的是基于电源的两长度哈希表,所以它增加了这个功能,以确保下位被充分地分布。

    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);


Answer 4:

为什么不使用CRC64多项式。 这些都是合理高效和优化,以确保所有的位进行计数,遍布结果空间。

有很多实现在网络上可用,如果你谷歌“CRC64的Java”



Answer 5:

做这样的事情:

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克隆是快了很多。



Answer 6:

字符串逆向获得另外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()方法不存在,将需要实施一些其他的方式。



Answer 7:

今天的答案(2018)。 SipHash。

这将是比大多数的答案快得多在这里,而且比所有这些品质显著较高。

番石榴库具有一个: https://google.github.io/guava/releases/23.0/api/docs/com/google/common/hash/Hashing.html#sipHash24--



Answer 8:

你看看阿帕奇公共朗 ?

但对于64位(128),你需要一些技巧:在书中有效的Java由约书亚布洛赫帮助所设定的规则创建64位散列容易(只使用int的长代替)。 对于128位,你需要额外的黑客...



Answer 9:

免责声明:如果您想有效地散列个人自然语言的单词本方案适用。 这是低效散列较长的文本,或含有非字母字符的文本。

我不知道的功能,但这里有一个想法,可能会有所帮助:

  • 奉献64位的52为表示该信存在于字符串。 例如,如果“A”是本你位设为[0],为“B”设定位1 ,对于“A”设置的位[26]。 这样,只有准确地包含相同的字母文本将具有相同的“签名”。

然后可以使用剩余的12位来编码字符串的长度(或它的模数值),以进一步减少冲突,或者使用传统的散列函数产生一个12位的散列码。

假设你输入的文字,只有我能想象这会导致极少数碰撞,将是经济的计算(为O(n))。 不像其他的解决方案,到目前为止,这种方法需要域名的问题考虑在内,以减少冲突 -它字谜探测器编程珍珠描述是基于关闭(见这里 )。



文章来源: What is a good 64bit hash function in Java for textual strings?