Java 在HashSet中插入对象的条件?

Java 在HashSet中插入对象的条件?,java,collections,hash,hashset,Java,Collections,Hash,Hashset,请容忍我,因为我正试图引入一个与许多活动线程直接矛盾的新概念 在HashSet中插入对象的条件是什么 查看源代码,它的目标是: if (e.hash == hash && ((k = e.key) == key || key.equals(k))) 完整代码位于: 因此,这取决于 哈希码 等于 ==即,如果它们是相同的对象。 现在,我们知道如果obj1.equalsobj2返回true,两个对象的hashcode必须相同。根据这3个参数的相对值,我创建了下表: 看看第四种情况

请容忍我,因为我正试图引入一个与许多活动线程直接矛盾的新概念

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

查看源代码,它的目标是:

if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
完整代码位于:

因此,这取决于

哈希码 等于 ==即,如果它们是相同的对象。 现在,我们知道如果obj1.equalsobj2返回true,两个对象的hashcode必须相同。根据这3个参数的相对值,我创建了下表:

看看第四种情况。尽管equals返回false,但对象将被添加到HashSet中。在所有其他情况下,当且仅当equals返回false时,才添加对象。因此,可以说忽略条件4,即是否将对象添加到HashSet的决定仅由equals方法决定。当被问及为什么要使用hashCode时,标准的回答是,它通过简单地比较整数来提高性能,因为短路运算符可以节省equals方法的执行。这个论点在许多线程中讨论过,比如

然而,我发现这个论点是不正确的。如果equals返回false,而==返回true,Hashcode实际上有一个决定要做。这是非常不可能的,因为同一个对象通常会为equals返回true,直到有人明确违反equals契约覆盖equals方法,从而使它为同一个对象返回不同的值。尽管如此,这还是有可能的,java似乎在某些违约代码的情况下提供了一种风险管理。拿你的

HashSet要求传递给它的对象遵守hashCode和equals的约定——如果不遵守,则垃圾输入垃圾输出。equals合同规定,如果两个引用为==,则它们必须相等。因此,上面的条件4违反了平等契约,因此违反了HashSet的契约,因此HashSet没有义务在呈现这样一组条件时采取有意义的行动


条件5也破坏了合同。

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

# 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行表示两个相等的项具有不同哈希代码的情况,这违反了哈希代码契约

最后两行7和8是非法的,因为它们违反了等于是自反的要求,即x.equalsx必须为所有非空x返回true

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

if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
e、 hash==出于效率的考虑,在这种情况下出现hash,它是在正常情况下执行的最快测试,用于在第一个关卡上对相等性进行折扣。在所有情况下,如果程序处于不违反==.equals.hashCode约定的有效状态,则对if语句的最终结果没有逻辑影响


不考虑因违反==.equals.hashCode契约而产生的条件,因为此类程序处于无效状态,并且未定义行为。违约合同下的影响可能会随着执行的不同而变化,因此不应依赖于执行。

您没有说明操作顺序。实际表格将包括DC,即不关心,因为它们不会被评估

如果我们在以下情况为假时添加

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

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

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 |

在问题表中;由于==vs等于合同,4号和5号合同无效。

您需要的只是有效条件,然后您可以直接进行此操作

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时添加。

@richardingle:很明显,如果你编写了一个伪equals/hashCode,那么你会得到伪结果。@qualtar是否有任何例子表明==.equals.hashCode契约没有被违反,其中。hashCode不仅仅是为了性能?因为在违反这种联系的地方,任何事情都可以做。这是为了效率,这是第一个条件
在if中,因为它最简单。事实上,如果你违反了合同,它会有第二种行为,这与此无关。当程序处于有效状态时,次级行为永远不会被看到,因此不被考虑。编辑不会做出决定。因此,基本上,您已经确定,如果程序完全被破坏,将出现虚假结果。你期待什么?它能正常工作吗?没有义务和没有义务不一样。这意味着您将任由实现细节摆布,这些细节可能会一时兴起而改变。@qualtar有效果!=具有有意义的效果。这只是一个坏系统发出的噪音我想到了整张桌子。行号1和行号2不是必需的,因为它们表明了一个明显的事实,即如果equals为true,hashcode就一定是相同的。@它们是qualtar,您可以编写一个无效的hashCode,这样如果equals为true,hashCode就不是same@RichardTingle:点…我还是忍不住忽略了第1行和第2行盲目地遵循相等。第4行也是无效的;如果两个对象相等,则hashCode值必须相同。好的……放手……因此,如果契约被破坏,则没有比较点,hashCode仅存在且仅用于性能……对吗???事实上,如果契约被破坏,则您将重写程序,因此它不是。我想一个有缺陷的程序是如何有缺陷的在学术层面上是很有趣的,但这样的程序永远不应该投入生产
╔════════════╦══════════╦═══════╦═══════╗
║ hashCode() ║ equals() ║  ==   ║ add() ║
╠════════════╬══════════╬═══════╬═══════╣
║ not-same   ║ false    ║ false ║ true  ║
║ same       ║ false    ║ false ║ true  ║
║ same       ║ true     ║ false ║ false ║
║ same       ║ true     ║ true  ║ false ║
╚════════════╩══════════╩═══════╩═══════╝