7

如果我将覆盖hashCode()方法会降低应用程序的性能。我在我的应用程序的许多地方都覆盖了这个方法。

4

8 回答 8

7

是的,如果 hashCode 方法的实现方式不好,您可能会降低散列集合的性能。hashCode 方法的最佳实现应该为唯一对象生成唯一的 hashCode。O(1)唯一的 hashCode 将避免冲突,并且可以复杂地存储和检索元素。但是只有 hashCode 方法是做不到的,还需要重写 equals 方法来帮助 JVM。

如果 hashCode 方法无法为唯一对象生成唯一哈希,那么您可能会在一个存储桶中保存多个对象。当您有两个具有相同哈希但 equals 方法为它们返回 false 的元素时,就会发生这种情况。所以每次发生这种情况时,元素都会被添加到哈希桶的列表中。这将减慢元素的插入和检索。这将导致O(n)get 方法的复杂性,其中 n 是桶中列表的大小。

注意:当您尝试在 hashCode 实现中为唯一对象生成唯一哈希时,请确保为此编写简单的算法。如果您生成散列的算法太重,那么您肯定会发现对散列集合的操作性能不佳。由于散列集合上的大多数操作都调用了 hashCode 方法。

于 2013-08-12T06:46:21.123 回答
3

如果在正确的位置使用正确的数据结构,它将提高性能,

例如:Object 中正确的哈希码实现几乎可以将 O(N) 转换为 O(1) 以进行HashMap查找

hashCode()除非你在方法中做了太多复杂的操作

hashCode()每次它必须用你的 Object 处理 Hash 数据结构并且如果你有沉重的hashCode()方法(不应该)时,它会调用方法

于 2013-08-12T06:39:30.150 回答
3

这完全取决于您如何实施hashCode. 如果您正在执行大量昂贵的深度操作,那么它可能会这样做,在这种情况下,您应该考虑缓存hashCode(就像这样String做)的副本。但是一个像样的实现,比如 with HashCodeBuilder,不会有什么大不了的。具有良好的值可以使在s 和shashCode等数据结构中的查找速度更快,并且如果您覆盖,则需要覆盖。HashMapHashSetequalshashCode

于 2013-08-12T06:41:52.260 回答
3

无论如何, JavahashCode()是一个虚函数,因此它被覆盖并且使用了被覆盖的方法这一事实并没有造成性能损失。

真正的区别可能是方法的实现。默认情况下,hashCode()像这样工作(source):

在合理可行的情况下,由 Object 类定义的 hashCode 方法确实为不同的对象返回不同的整数。(这通常通过将对象的内部地址转换为整数来实现,但 JavaTM 编程语言不需要这种实现技术。)

因此,只要您的实现如此简单,就不会有性能损失。但是,如果您基于许多字段执行复杂的计算操作,调用许多其他函数 - 您会注意到性能损失,但这只是因为您hashCode()做了更多的事情。

还有hashCode()执行效率低下的问题。例如,如果您hashCode()只是返回值1,那么使用HashMaporHashSet将比正确实现慢得多。有一个很好的问题涵盖了实现hashCode()equals()关于 SO 的主题:在 Java 中覆盖 equals 和 hashCode 时应该考虑哪些问题?

还有一点需要注意:请记住,无论何时实施,hashCode()您都应该实施equals(). 此外,你应该小心,因为如果你写一个无效的hashCode(),你可能会破坏各种集合的相等性检查。

于 2013-08-12T06:49:54.197 回答
2

在类中重写 hashCode() 本身不会导致任何性能问题。但是,当此类类的实例被插入到 HashMap HashSet 或等效数据结构 hashCode() 和可选的 equals() 方法时,将调用该方法来识别将元素放入的正确存储桶。同样适用于检索搜索和删除。

正如其他人发布的那样,性能完全取决于 hashCode() 的实现方式。但是,如果根本不使用特定类的 equals 方法,则不必覆盖 equals() 和 hashCode() ,但如果 equals() 被覆盖,则 hashcode() 也必须被覆盖

于 2013-08-13T21:52:18.307 回答
1

正如前面所有评论所提到的,哈希码用于集合中的哈希,或者它可以用作 equals 中的否定条件。所以,是的,你可以大大减慢你的应用程序。显然还有更多的用例。

首先,我要说的方法(是否重写它)取决于你正在谈论的对象的类型。

  1. 哈希码的默认实现尽可能快,因为它对每个对象都是唯一的。对于许多情况来说可能就足够了。
  2. 当您想使用 hashset 并且不想在集合中存储两个相同的对象时,这并不好。现在,重点是“相同”这个词。

“相同”可以表示“相同的实例”。当您的对象是实体时,“相同”可以表示具有相同(数据库)标识符的对象,或者“相同”可以表示具有所有相同属性的对象。到目前为止,它似乎会影响性能。

但是其中一个属性也可以是一个可以按需评估 hashCode() 的对象,现在您可以在对根对象调用 hash-code 方法时始终获得对象树的 hash-code 评估。

那么,我会推荐什么?你需要定义和澄清你想要做什么。你真的需要区分不同的对象实例,还是标识符很关键,或者它是值对象?

它还取决于不变性。可以在使用所有构造函数属性(只有 get)构造对象时计算一次哈希码值,并在调用 hashcode() 时始终使用它。或者另一种选择是在任何属性发生变化时始终计算哈希码。您需要决定大多数情况下是读取值还是写入值。

我要说的最后一件事是仅在您知道自己需要它并且知道自己在做什么时才覆盖 hashCode() 方法。

于 2013-08-12T07:15:53.837 回答
0

如果您将覆盖 hashCode() 方法会降低应用程序的性能。如果在正确的位置使用正确的数据结构,它将提高性能,

例如:Object 中正确的 hashcode() 实现几乎可以将 O(N) 转换为 O(1) 以进行 HashMap 查找。除非您在 hashCode() 方法中执行了太多复杂的操作

于 2013-08-12T06:57:39.160 回答
0

hashCode 方法的主要目的是允许一个对象成为哈希映射中的键或哈希集的成员。在这种情况下,对象也应该实现 equals(Object) 方法,这与 hashCode 实现是一致的:

If a.equals(b) then a.hashCode() == b.hashCode()

如果 hashCode() 在同一个对象上被调用两次,它应该返回相同的结果,前提是对象没有被改变

从性能角度看hashCode

  • 从性能的角度来看,hashCode 方法实现的主要目标是尽量减少共享相同哈希码的对象数量。
  • 所有基于 JDK 哈希的集合都将它们的值存储在一个数组中。
  • 哈希码用于计算此数组中的初始查找位置。之后,equals 用于将给定值与存储在内部数组中的值进行比较。因此,如果所有值都有不同的哈希码,这将最大限度地减少哈希冲突的可能性。
  • 另一方面,如果所有值都具有相同的哈希码,则哈希映射(或集合)将降级为一个列表,其上的操作具有 O(n2) 复杂度。
  • 从 Java 8 开始,虽然碰撞不会像在早期版本中那样影响性能,因为在阈值之后,链表将被二叉树替换,与 O 相比,在最坏的情况下,这将为您提供 O(logN) 性能(n) 的链表。
  • 永远不要编写返回常量的 hashCode 方法。
  • String.hashCode 结果分布几乎是完美的,因此您有时可以用它们的哈希码替换字符串。

下一个目标是检查您还有多少具有非唯一代码的标识符。如果您有太多非唯一哈希码,请改进您的 hashCode 方法或增加允许的哈希码值范围。在完美的情况下,您的所有标识符都将具有唯一的哈希码。

于 2019-01-25T21:20:11.307 回答