字符串的良好哈希函数

时间:2010-04-12 17:57:26

标签: java hash hashtable hashcode

我正在尝试为字符串设想一个好的哈希函数。而且我认为总结字符串中前五个字符的unicode值可能是一个好主意(假设它有五个,否则在它结束时停止)。这是一个好主意,还是一个坏主意?

我在Java中这样做,但我不认为这会产生很大的不同。

15 个答案:

答案 0 :(得分:142)

通常哈希不会执行总和,否则stoppots将具有相同的哈希值。

并且您不会将其限制为前n个字符,因为否则房屋和房屋将具有相同的哈希值。

通常哈希值取值并乘以素数(使其更有可能产生唯一的哈希值)所以你可以这样做:

int hash = 7;
for (int i = 0; i < strlen; i++) {
    hash = hash*31 + charAt(i);
}

答案 1 :(得分:128)

如果这是一个安全问题,你可以使用Java加密:

import java.security.MessageDigest;

MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(stringToEncrypt.getBytes());
String encryptedString = new String(messageDigest.digest());

答案 2 :(得分:36)

您应该使用String.hashCode()

如果你真的想自己实现hashCode:

  

不要试图排除   一个物体的重要部分来自   要改进的哈希码计算   表现 - Joshua Bloch,Effective Java

仅使用前五个字符是坏主意。考虑层次名称,例如URL:它们都将具有相同的哈希码(因为它们都以“http://”开头,这意味着它们存储在哈希映射中的同一个桶中,表现出糟糕的性能。 / p>

这是一篇关于来自“Effective Java”的String hashCode的战争故事:

  

实现了String散列函数   在1.2检查之前的所有版本中   最多16个字符,均匀   在整个弦乐中间隔开始   与第一个字符。对于大   层次名称的集合,   比如URL,这个哈希函数   显示出可怕的行为。

答案 3 :(得分:17)

如果你是用Java做的,那么你为什么要这样做呢?只需在字符串

上调用.hashCode()即可

答案 4 :(得分:12)

Guava's HashFunctionjavadoc)提供了不错的非加密哈希值。

答案 5 :(得分:7)

Nick提供的这个函数很好但是如果你使用新的String(byte [] bytes)来转换为String,它就失败了。您可以使用此功能来执行此操作。

private static final char[] hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

public static String byteArray2Hex(byte[] bytes) {
    StringBuffer sb = new StringBuffer(bytes.length * 2);
    for(final byte b : bytes) {
        sb.append(hex[(b & 0xF0) >> 4]);
        sb.append(hex[b & 0x0F]);
    }
    return sb.toString();
}

public static String getStringFromSHA256(String stringToEncrypt) throws NoSuchAlgorithmException {
    MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
    messageDigest.update(stringToEncrypt.getBytes());
    return byteArray2Hex(messageDigest.digest());
}

可能这可以帮助某人

答案 6 :(得分:5)

// djb2 hash function
unsigned long hash(unsigned char *str)
{
    unsigned long hash = 5381;
    int c;

    while (c = *str++)
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

    return hash;
}

source Logic behind djb2 hash function - SO

答案 7 :(得分:4)

据传,

FNV-1是字符串的良好哈希函数。

对于长字符串(长度大于200个字符),您可以从MD4哈希函数中获得良好的性能。作为一种加密功能,它在大约15年前被破坏了,但是对于非加密目的,它仍然非常好,而且速度惊人。在Java的上下文中,您必须将16位char值转换为32位字,例如通过将这些值分组成对。可以在sphlib中找到Java中MD4的快速实现。在课堂作业的背景下可能有些过分,但值得一试。

答案 8 :(得分:3)

如果您想查看行业标准实施,我会查看java.security.MessageDigest

“消息摘要是安全的单向散列函数,它采用任意大小的数据并输出固定长度的散列值。”

答案 9 :(得分:2)

sdbm:此算法是为sdbm(ndbm的公共域重新实现)数据库库创建的

static unsigned long sdbm(unsigned char *str)
{   
    unsigned long hash = 0;
    int c;
    while (c = *str++)
            hash = c + (hash << 6) + (hash << 16) - hash;

    return hash;
}

答案 10 :(得分:1)

这里的a link解释了许多不同的哈希函数,现在我更喜欢ELF哈希函数来解决你的特定问题。它将任意长度的字符串作为输入。

答案 11 :(得分:0)

         public String hashString(String s) throws NoSuchAlgorithmException {
    byte[] hash = null;
    try {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        hash = md.digest(s.getBytes());

    } catch (NoSuchAlgorithmException e) { e.printStackTrace(); }
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < hash.length; ++i) {
        String hex = Integer.toHexString(hash[i]);
        if (hex.length() == 1) {
            sb.append(0);
            sb.append(hex.charAt(hex.length() - 1));
        } else {
            sb.append(hex.substring(hex.length() - 2));
        }
    }
    return sb.toString();
}

答案 12 :(得分:0)

这将避免任何碰撞,并且在我们使用计算中的移位之前它会很快。

 int k = key.length();
    int sum = 0;
    for(int i = 0 ; i < k-1 ; i++){
        sum += key.charAt(i)<<(5*i);
    }

答案 13 :(得分:-1)

在尝试为字符串开发一个好的hast函数时,使用奇数是一个好主意。这个函数接受一个字符串并返回一个索引值,到目前为止它的工作还不错。并且碰撞较少。该指数的范围从0到300甚至可能更高,但到目前为止,即使是像“机电工程”这样的长话,我还没有得到任何更高的数据

int keyHash(string key)
{
    unsigned int k = (int)key.length();
    unsigned int u = 0,n = 0;

    for (Uint i=0; i<k; i++)
    {
        n = (int)key[i];
        u += 7*n%31;
    }
    return u%139;
}

你可以做的另一件事是将每个字符的int解析乘以索引,因为它增加像“bear”这样的单词 (0 * b)+(1 * e)+(2 * a)+(3 * r)这将给你一个int值。上面的第一个哈希函数在“here”和“hear”处发生碰撞但仍然很好地给出了一些很好的唯一值。下面的一个不会与“here”和“hear”发生碰撞,因为我会将每个字符与索引相乘而增加。

int keyHash(string key)
{
    unsigned int k = (int)key.length();
    unsigned int u = 0,n = 0;

    for (Uint i=0; i<k; i++)
    {
        n = (int)key[i];
        u += i*n%31;
    }
    return u%139;
}

答案 14 :(得分:-1)

这是我用于构建的哈希表的简单哈希函数。它基本上用于获取文本文件并将每个单词存储在表示字母顺序的索引中。

int generatehashkey(const char *name)
{
        int x = tolower(name[0])- 97;
        if (x < 0 || x > 25)
           x = 26;
        return x;
}

这基本上是根据他们的第一个字母对词进行散列。所以,以&#39; a&#39;开头的单词会得到一个0的哈希键,&#39; b&#39;得到1依旧等等&#39; z&#39;数字和符号的散列密钥为26.这是一个优点;您可以轻松快速地计算给定单词在哈希表中的索引位置,因为它全部按字母顺序排列,如下所示: 代码可以在这里找到:https://github.com/abhijitcpatil/general

  

给出以下文字作为输入:阿迪克斯有一天对杰姆说,“我宁愿你在后院用锡罐射击,但我知道你会去   在鸟之后。拍你想要的所有蓝鸟,如果你能击中他们,但是   记住杀死一只模仿鸟是一种罪过。“那是我唯一的一次   曾经听说阿迪克斯说做某事是犯罪,我问小姐   Maudie,关于它。 “你父亲是对的,”她说。 “模仿鸟不会   做一件事,除了让音乐让我们享受。他们不吃饭   人们的花园,不要在玉米婴儿床上筑巢,他们不做一件事   但为我们唱出他们的心。这就是为什么杀死一个人是犯罪的原因   模仿鸟。

这将是输出:

0 --> a a about asked and a Atticus a a all after at Atticus
1 --> but but blue birds. but backyard
2 --> cribs corn can cans
3 --> do don’t don’t don’t do don’t do day
4 --> eat enjoy. except ever
5 --> for for father’s
6 --> gardens go
7 --> hearts heard hit
8 --> it’s in it. I it I it’s if I in
9 --> jays Jem
10 --> kill kill know
11 --> 
12 --> mockingbird. music make Maudie Miss mockingbird.”
13 --> nest
14 --> out one one only one
15 --> people’s
16 --> 17 --> right remember rather
18 --> sin sing said. she something sin say sin Shoot shot said
19 --> to That’s their thing they They to thing to time the That to the the tin to
20 --> us. up us
21 --> 
22 --> why was was want
23 --> 
24 --> you you you’ll you
25 --> 
26 --> “Mockingbirds ” “Your ‘em “I’d