如何制作有效的hashCode?

时间:2015-08-28 10:09:50

标签: java performance hashcode

我有三个hashCode方法如下,我根据它们的效率对它们进行了优先级排序。我想知道是否有任何其他方法来制作更有效的hashCode方法。

1) public int hashCode() { //terrible
     return 5; 
   }
2) public int hashCode() { //a bit less terrible
     return name.length; 
   }
3) public int hashCode() { //better
     final int prime = 31;
     int result = 1;
     result = prime * result + ((name == null) ? 0 : name.hashCode());
     return result;
   }

5 个答案:

答案 0 :(得分:6)

没有万无一失的方法来保证您的hashcode功能是最佳的,因为它是通过两种不同的指标来衡量的。

  • 效率 - 计算速度有多快。
  • 碰撞 - 碰撞的几率是多少。

您:

  1. 以牺牲碰撞为代价最大限度地提高效率。
  2. 在中间找到一个位置 - 但仍然不好。
  3. 效率最低,但最好避免碰撞 - 仍然不一定是最好的。
  4. 你必须自己找到平衡点。

    有时很明显,一种非常有效的方法永远不会发生碰撞(例如ordinal的{​​{1}}。

    有时候记住这些值是一个很好的解决方案 - 这种方式甚至可以减轻非常低效的方法,因为它只能计算一次。这有明显的成本,也必须平衡。

    有时,代码的整体功能有助于您的选择。假设您要将enum个对象放在File中。有很多选择是明确的:

    1. 使用文件名的哈希码。
    2. 使用文件路径的哈希码。
    3. 使用文件内容的crc。
    4. 使用文件内容的SHA1摘要的哈希码。
    5. 为什么碰撞不好

      HashMap的一个主要用途是将对象插入hashcode。该算法从对象请求哈希码并使用它来决定将对象放入哪个桶。如果哈希与另一个对象冲突,则该桶中将存在另一个对象,在这种情况下,桶将不得不增长,这会花费时间。如果所有哈希都是唯一的,那么地图将是每个桶一个项目,因此最大效率。

      有关HashMap如何运作的深入讨论,请参阅有关Hash Table的优秀WikiPedia文章。

答案 1 :(得分:4)

  

我根据他们的效率优先考虑他们

您的列表按升序效率排序 - 如果“效率”表示应用程序的性能,而不是hashCode方法与其他所有方法隔离的延迟。分散性差的哈希码将导致通过HashMap内的链表进行线性或近线性搜索,完全取消哈希表的优点。

特别要注意的是,在今天的架构中,计算比指针解引用便宜得多,而且它的成本低于固定。单个高速缓存未命中值得一千个简单的算术运算,并且每个指针解除引用都是潜在的高速缓存未命中。

答案 2 :(得分:2)

我的回答是走另一条道路 - 基本上不是答案,而是一个问题:为什么你担心hashCode()的表现?

您是否对应用程序进行了详尽的分析,发现某些对象的某个方法存在性能问题?

如果该问题的答案是“否”......那么 - 为什么你认为你需要担心这一个方法?为什么你认为eclipse生成的默认值可能每天使用数十亿次......对你来说还不够好?

请参阅here,了解为什么在这些问题上浪费时间总是一个非常糟糕的主意。

答案 3 :(得分:2)

除了目前为止有价值的答案外,我还想补充一些其他方法:

3a)的

public int hashCode() {
     return Objects.hashCode(name);
}

在性能方面没有多少优点/缺点,但更简洁。

4。)您应该提供有关您正在谈论的课程的更多信息,或重新考虑您的设计。但是当此类的 only 属性为String时,使用类作为哈希映射的键,那么可能也可以使用字符串直接。因此选项4是:

// Changing this...
Map<Key, Value> map;
map.put(key, value);
Value value = map.get(key);

// ... to this:
Map<String, Value> map;
map.put(key.getName(), value);
Value value = map.get(key.getName());

(如果这是不可能的,因为Key的&#34;名称&#34;可能会在创建之后发生变化,无论如何你都会遇到更大麻烦 - 请参阅下一点)

5.。)也许你可以预先计算哈希码。实际上,这也是在java.lang.String类:

中完成的
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    ...

    /** Cache the hash code for the string */
    private int hash; // Default to 0

但是,当然,这个只适用于不可变类。您应该意识到使用可变类作为Map的键是&#34;危险&#34;并且可能导致一致性错误,并且只有在您完全确定用作密钥的实例不会发生变化时才会出现。

所以如果你希望使用你的类作为键,并且你的类甚至可以有更多的字段而不是单个字段,那么你可以将哈希码存储为字段:

class Key 
{
    private final String name;
    ... // Other fields...

    private final int hashCode;

    Key(String name, ...)
    {
        this.name = name;
        ... // Other fields

        // Pre-compute and store the hash code:
        this.hashCode = computeHashCode();
    }


    private int computeHashCode()
    {
        int result = 31;
        result = 31 * result + Objects.hashCode(name);
        result = 31 * result + ... // Other fields
        return result;
    }
}

答案 4 :(得分:0)

是的,有更好的选择。

xxHashMurmurHash3是通用哈希算法,质量更快,质量更好。