1

请耐心等待,因为我试图引入一个与许多活动线程直接矛盾的新概念。

HashSet中插入对象的条件是什么?

查看源代码,它归零为:

if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

完整代码在:HashSet.java

所以,这取决于

  1. 哈希码
  2. 等于()
  3. == 即如果它们是相同的对象。

现在,我们知道如果 obj1.equals(obj2) 返回 true,则两个对象的哈希码必须相同。基于这 3 个参数的相对值,我创建了下表: HashCode添加条件

看条件号。4. 尽管 equals() 返回 false,对象被添加到 HashSet。在所有其他情况下,当且仅当 equals() 返回 false 时才添加对象。因此,可以说(忽略条件号 4),是否将对象添加到 HashSet 的决定仅由 equals() 方法决定。当被问及为什么我们使用 hashCode() 时,标准的回答是它通过简单地比较整数来提高性能,因为短路运算符节省了 equals() 方法的执行。这个论点在许多线程中都有讨论,例如如果我们无论如何都要检查等于,为什么要检查哈希?

但是,我发现这个论点是不正确的。如果 equals() 返回 false 并且 == 返回 true,Hashcode 实际上会做出决定。这是极不可能的,因为相同的对象通常为 equals() 返回 true,直到有人明确(违反 equals() 协定)覆盖 equals 方法,以便它为相同的对象返回不同的值。尽管如此,它还是有可能的,并且 java 似乎在某些违约代码的情况下提供了风险管理。你拿!

4

6 回答 6

4

HashSet要求传递给它的对象遵守hashCodeequals- 如果他们不遵守合同,那么垃圾进垃圾出。的合同equals规定,如果两个引用是==,它们必须相等。因此,您上面的条件 4 违反了平等合同,因此违反了 合同HashSet,因此HashSet在提出这样一组条件时没有义务采取有意义的行动。

条件 5 也违反了合同。

于 2013-11-06T16:44:54.787 回答
2

合同equals()

如果两个引用相等或相同 (==),equals()则应返回 true。

合同hashCode()

如果equals()方法返回true两个对象,则hashCode()还需要为这两个对象返回相同的哈希值。

真值表

因此,让我们考虑 8 个场景的真值表,只有 4 个有效场景如下所示。

| hashCode() | equals() |   ==   | add() |
| not-same   | false    | false  | true  |
| not-same   | false    | true   |   -   | - INVALID scenario (== vs equals)
| not-same   | true     | false  |   -   | - INVALID scenario (hash vs equals)
| not-same   | true     | true   |   -   | - INVALID scenario (hash vs equals)
| same       | false    | false  | true  |
| same       | false    | true   |   -   | - INVALID scenario (== vs equals)
| same       | true     | false  | false | 
| same       | true     | true   | false |

在问题表中;S.No 4 & 5 由于==vsequals()合同无效。

于 2013-11-06T17:25:19.017 回答
1

你的真值表不完整。它应该有八行,如下所示:

# HashCode Equals  ==    add()
- -------- ------ ------ -----
1   same     TRUE  TRUE  FALSE
2   same     TRUE FALSE  FALSE
3   same    FALSE FALSE   TRUE
4   diff    FALSE FALSE   TRUE
======= ILLEGAL ROWS =========
5   diff     TRUE  TRUE   TRUE -- Breaks the contract of hashCode, which must
                               -- return the same value on multiple calls
6   diff     TRUE FALSE   TRUE -- Breaks the contract of hashCode
7   same    FALSE  TRUE  FALSE -- Breaks the contract of equals
8   diff    FALSE  TRUE  FALSE -- Breaks the contract of equals

第 5 行表示hashCode当您多次调用它时返回不同值的情况(这是一件非常糟糕的事情,但当对象是可变的时可能偶尔会发生)。

第 6 行表示两个相同项目不同hashCode的情况 - 违反hashCode合同。

最后两行 #7 和 #8 是非法的,因为它们违反了equals()自反的要求(即x.equals(x)必须返回true所有非 null x)。

表中的第 4 行和第 5 行表示非法状态。HashSet但是,永远不会发现,因为 of 的第一个子句OR仅仅是优化。由于短路,不会调用equalswhen==评估为true,因此HashSet有效地假设 的自反性equals,即使实现是错误的。

于 2013-11-06T16:52:12.470 回答
1
if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

e.hash == hash出于效率的原因,它存在于这种情况下,它是(在正常情况下)执行最快的测试,并且用于在第一关时打折平等。在程序处于有效状态(不违反== .equals() .hashCode()合同)的所有情况下,它对 if 语句的最终结果没有逻辑影响。

== .equals() .hashCode()由于此类程序处于无效状态并且未定义行为,因此不考虑因违反合同而导致的条件。违约合同下的影响可能会从一个实施变为另一个实施,因此永远不应依赖。

于 2013-11-06T16:56:31.917 回答
0

您没有解决操作顺序。真实表将包括 DC(即 Don't Care),因为它们不会被评估。

如果我们在以下为 FALSE 时添加

if (e.hash == hash && ((k = e.key) == key || key.equals(k)))

然后

hash==  && (key== || equals()) >> add?
---------------------------------------
   T     |    T    |   DC     ||  F
   F     |    DC   |   DC     ||  T
   T     |    F    |   T      ||  F  <- Not possible with correct code
   T     |    F    |   F      ||  F  <- Not possible with correct code

如果 hash 或 equals 函数不正确,那么这些都不重要。

于 2013-11-06T17:14:55.830 回答
0

所有你需要的是有效条件然后你可以直接去这个。

if(!Obj1.equals(Obj2)) add() ;

如您所见,有 8 种可能的情况,其中 4 种是唯一有效的,让我们继续处理它们。

╔════════════╦══════════╦═══════╦═══════╗
║ hashCode() ║ equals() ║  ==   ║ add() ║
╠════════════╬══════════╬═══════╬═══════╣
║ not-same   ║ false    ║ false ║ true  ║
║ same       ║ false    ║ false ║ true  ║
║ same       ║ true     ║ false ║ false ║
║ same       ║ true     ║ true  ║ false ║
╚════════════╩══════════╩═══════╩═══════╝

所以现在很明显我们只在 equals() 为 false 时添加。

于 2013-11-06T20:05:55.903 回答