【问题标题】:Is it a good approach to generate hash codes?这是生成哈希码的好方法吗?
【发布时间】:2011-10-01 07:14:50
【问题描述】:

我必须写一个哈希函数,在以下两种情况下:

  • 我对传递给该方法的Object o 一无所知 - 它可以是字符串、整数或实际的自定义对象;
  • 我根本不允许打电话给hashCode()

我现在使用的计算哈希码的方法:

  1. 将对象写入字节流;
  2. 将字节流转换为字节数组;
  3. 遍历字节数组并通过执行以下操作计算哈希:

    hash = hash * PRIME + byteArray[i]

我的问题是这是一种可以通过的方法,有没有办法改进它?我个人觉得这个功能的范围太广了——没有关于对象是什么的信息,但在这种情况下我几乎没有发言权。

【问题讨论】:

  • 这听起来像是功课(为什么你不能打电话给hashCode?)。如果是,请标记它,以便您得到不同(并且更好)的答案。
  • 谢谢,不知道这个标签存在 :) 完成。
  • 可以完全摆脱第 2 步 :)
  • @Blindy 我认为他的意思是将字节流转换为字节数组。
  • @Nikita 因为你不知道任何对象的细节,我认为你正在做最好的方式。

标签: java hash-code-uniqueness


【解决方案1】:

看看Bob Jenkin's article 的非加密哈希。他介绍了多种方法,并讨论了它们的优势、劣势以及速度和碰撞概率之间的权衡。

如果不出意外,它将允许您证明您的算法决定是正确的。向您的教练解释为什么您选择速度而不是正确性,反之亦然。

作为起点,试试他的One-at-a-time hash

ub4 one_at_a_time(char *key, ub4 len)
{
  ub4   hash, i;
  for (hash=0, i=0; i<len; ++i)
  {
    hash += key[i];
    hash += (hash << 10);
    hash ^= (hash >> 6);
  }
  hash += (hash << 3);
  hash ^= (hash >> 11);
  hash += (hash << 15);
  return (hash & mask);
} 

它很简单,但在处理更复杂的算法时却出奇的好。

【讨论】:

    【解决方案2】:

    另外,如果您想尽可能避免冲突,您可以在第 3 步中使用标准化(如果故意冲突是一个问题,则使用加密)哈希函数,例如 SHA-2 左右?

    看看DigestInputStream,这也省去了你的第2步。

    【讨论】:

    • 固定,“将字节流转换为字节数组”——这样我就可以对其进行迭代。
    • -1 第 2 步显然是一个错字,使用 any 散列函数会抛出唯一性——它是散列而不是压缩。
    • @Nikita 您不需要创建字节数组(这将占用大量内存;大多数哈希函数都被指定为处理短​​缓冲区流)。用DigestInputStream更新了答案。
    • @Blindy 是的,这表达得很糟糕。我的意思是相对较低的碰撞率(并且使用 SHA2 或更好的,任何碰撞的概率小到天文数字永远(除非密码分析取得进展))。更新的版本还是不正确吗?
    • normalcryptographic 散列函数(假设散列输出大小相同)之间的主要区别在于,加密函数可以防止 intentional 碰撞,而普通散列只能防止意外碰撞。并且普通的哈希值通常更快。
    【解决方案3】:

    序列化方法仅适用于实际上可序列化的对象。因此,对于所有类型的对象来说是不可能的。

    此外,这通过比较对象具有等效的对象图,这不一定与 通过.equals() 相等。

    例如,由相同代码(具有相同数据)创建的 StringBuilder 对象将具有相等的 OOS 输出(即也相等的哈希),而 b1.equals(b2) 为 false,具有相同元素的 ArrayList 和 LinkedList 将被注册为不同,而 list1.equals(list2)true


    您可以通过创建自定义HashOutputStream 来避免将字节流转换为数组 步骤,该HashOutputStream 只需获取字节数据并对其进行哈希处理,而不是将其保存为数组以供以后迭代。

    class HashOutputStream extends OutputStream {
    
        private static final int PRIME = 13;
        private int hash;
    
        // all the other write methods delegate to this one
        public void write(int b) {
            this.hash = this.hash * PRIME + b;
        }
    
        public int getHash() {
            return hash;
        }
    }
    

    然后将您的 ObjectOutputStream 包裹在此类的对象周围。

    您可以查看其他校验和算法,而不是您的 y = y*13 + x 方法。例如,java.util.zip 包含Adler32(用于zlib 格式)和CRC32(用于gzip 格式)。

    【讨论】:

    • 为了解决并非所有对象都可序列化的问题,可以使用 toString() 将对象转换为字符串。看起来对象是传入hashCode方法的,所以这不是Object的hashCode方法,所以不需要维护equals一致性。
    【解决方案4】:

    您可以使用HashCodeBuilder.reflectionHashCode 而不是实现自己的解决方案。

    【讨论】:

    • 必须实现我自己的解决方案,这就是问题的关键:)
    【解决方案5】:

    hash = (hash * PRIME + byteArray[i]) % MODULO ?

    【讨论】:

    • 不需要 - 隐含模数为 2^64。
    • @mlaw 取决于内部表示。但你是对的,很可能是int
    猜你喜欢
    • 2011-03-08
    • 1970-01-01
    • 2013-04-11
    • 2011-04-28
    • 2011-04-10
    • 2010-11-07
    • 2014-01-01
    • 1970-01-01
    相关资源
    最近更新 更多