Java重写hashCode()方法有任何性能问题吗?

时间:2013-08-12 06:38:57

标签: java

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

8 个答案:

答案 0 :(得分:6)

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

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

注意:当您尝试为hashCode实现中的唯一对象生成唯一哈希时,请确保编写简单的算法来执行此操作。如果用于生成哈希的算法太重,那么您肯定会看到哈希集合上的操作性能很差。因为哈希集合上的大多数操作都会调用hashCode方法。

答案 1 :(得分:3)

如果在正确的位置使用正确的数据结构,

会提高性能

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

除非你在hashCode()方法

中做了太复杂的操作

每次必须使用对象处理哈希数据结构时,如果你有大量的hashCode()方法(不应该这样),它会调用hashCode()方法

答案 2 :(得分:3)

这完全取决于您如何实施hashCode。如果您正在进行大量昂贵的深度操作,那么可能也是如此,在这种情况下,您应该考虑缓存hashCode的副本(如String那样)。但是一个体面的实现,例如HashCodeBuilder,将不会是一个大问题。拥有良好的hashCode值可以使HashMapHashSet等数据结构中的查找速度更快,如果覆盖equals,则需要覆盖{{} 1}}。

答案 3 :(得分:3)

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

真正的区别可能是该方法的实施。默认情况下,hashCode()的工作方式如下(source):

  

尽可能合理实用,由hashCode方法定义   class Object确实为不同的对象返回不同的整数。 (这个   通常通过转换内部地址来实现   将对象转换为整数,但这种实现技术不是   JavaTM编程语言所要求的。)

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

还存在低效hashCode()实施的问题。例如,如果您的hashCode()只返回值1,那么使用HashMapHashSet的速度将明显慢于正确实施。有一个很好的问题涉及在SO上实施hashCode()equals()的主题:What issues should be considered when overriding equals and hashCode in Java?

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

答案 4 :(得分:2)

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

由其他人发布的表现完全取决于hashCode()的实现方式。 但是,如果根本不使用特定类的equals方法,则不必重写equals()和hashCode(),但如果重写equals(),则必须重写hashcode()

答案 5 :(得分:1)

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

首先,我会说这种方法(是否重写它)取决于你所谈论的对象的类型。

  1. 哈希代码的默认实现尽可能快,因为它对每个对象都是唯一的。对于很多情况来说,这已经足够了。
  2. 当您想要使用hashset并且假设不想在集合中存储两个相同的对象时,这不是很好。现在,重点在于“相同”一词。
  3. “相同”可以表示“相同实例”。当您的对象为entity或“相同”可以表示具有所有相同属性的对象时,“相同”可以表示具有相同(数据库)标识符的对象。到目前为止它似乎会影响性能。

    但是其中一个属性可以是一个可以根据需要评估hashCode()的对象,现在,当您在根对象上调用hash-code方法时,您总能得到对象树的哈希码的评估。

    那么,我会推荐什么?您需要定义并阐明您想要做的事情。你真的需要区分不同的对象实例,或者标识符是至关重要的,还是它是值对象?

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

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

答案 6 :(得分:1)

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

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

如果在同一对象上两次调用hashCode(),则只要不更改对象,它应该返回相同的结果

从性能的角度来看

hashCode

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

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

答案 7 :(得分:0)

如果你将覆盖hashCode()方法,它会降低application的性能。如果在正确的地方使用了正确的数据结构,它会提高性能,

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