在HashSet中插入对象的条件?

时间:2013-11-06 16:39:04

标签: java collections hash hashset

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

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

查看源代码,它会显示为:

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

完整代码:HashSet.java

所以,这取决于

  1. Hashcode方法
  2. 等于()
  3. ==即如果它们是相同的对象。
  4. 现在,我们知道如果obj1.equals(obj2)返回true,则两个对象的哈希码必须相同。基于这3个参数的相对值,我创建了下表: HashCode adding condition

    看条件没有。 4.尽管equals()返回false,但对象被添加到HashSet中。在所有其他情况下,当且仅当equals()返回false时,才会添加对象。因此,可以说(忽略条件号4)是否将对象添加到HashSet的决定仅通过equals()方法。在被问到为什么我们使用hashCode()时,标准答案是它通过简单地比较整数来提高性能,因为短路操作符保存了equals()方法的执行。许多线程如Why do we check hash if we are going to check equals anyways?

    讨论了这个论点

    但是,我发现这个论点是不正确的。如果equals()返回false并且==返回true,则Hashcode实际上决定采用。这是非常不可能的,因为同一个对象通常对equals()返回true,直到有人显式(违反equals()契约)覆盖equals方法,以便它为同一个对象返回不同的值。尽管如此,它有可能和java似乎在一些违约代码的情况下提供风险管理。你的服用!

6 个答案:

答案 0 :(得分:4)

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

条件5也违反了合同。

答案 1 :(得分: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由于== vs equals()合同而无效。

答案 2 :(得分: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 x)。

表中的第4行和第5行代表非法状态。 HashSet永远不会发现,因为OR的第一个条款仅仅是优化。由于短路,equals评估为==时无法调用true,因此HashSet有效地假定equals的反身性,即使实施是错误的。

答案 3 :(得分:1)

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

e.hash == hash出现在这种情况下是出于效率的原因,它是(在理智的情况下)最快的测试,用于在第一道障碍时折扣平等。在程序处于有效状态(不违反== .equals() .hashCode()合同)的所有情况下,它对if语句的最终结果没有逻辑影响。

不考虑违反== .equals() .hashCode()合同的条件,因为此类程序处于无效状态且未定义行为。合同破裂后的影响可能会从实施转变为实施,因此永远不应该依赖。

答案 4 :(得分:0)

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

如果我们添加以下内容为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函数不正确,那么这一切都不重要。

答案 5 :(得分: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时添加。