HashSet.contains 不应该返回 false

por*_*che 5 java hash equals hashcode hashset

我有这个代码:

public class Tray {

private Set<Block> blocks;

private int numColumns;
private int numRows;

//constructor
public Tray (int numRows, int numColumns){
    this.numColumns = numColumns;
    this.numRows = numRows;
    blocks = new HashSet<>();
}

public boolean satisfiesGoal(Tray other){

    Block thisBlock = this.blocks.iterator().next();
    Block otherBlock = other.blocks.iterator().next();

    boolean hashesEqual = thisBlock.hashCode() == otherBlock.hashCode(); // this is true

    boolean areEqual = thisBlock.equals(otherBlock) && otherBlock.equals(thisBlock); // this is true

    boolean contains = this.blocks.contains(otherBlock); // this is false, why?
    return contains;
}
Run Code Online (Sandbox Code Playgroud)

在主要方法中,我已将 2 个块添加到各自的托盘中。根据调试器,变量“hashesEqual”和“areEqual”为真,但布尔值“contains”为假。关于为什么根据“equals”方法,2 个对象的哈希值会相等,但不会在 HashSet 中包含相等对象的任何想法?

Boa*_*ann 11

如果在将对象添加到 HashSet以影响它们的相等性和哈希码的方式修改对象,则会发生此问题。由于未在哈希表的正确槽中找到与其新值相对应的对象,该集合将出现故障。

同样,如果您修改用作键的对象,HashMap 也会发生故障。与 TreeSet 和 TreeMap 类似。所有这些数据结构都可以快速定位对象,因为每个对象的值决定了它的存储位置。如果这些对象随后被修改,结构就会出错。

不可变对象作为集合元素和映射键更好,因为它们避免了这种复杂性。

如果您必须修改属于对象相等性一部分的字段,则需要先将其暂时从集合中删除,然后再添加。或者,使用列表作为对象的主要容器,并仅在需要时构造一个临时集。