【问题标题】:Implementing hashcode and equals for custom classes为自定义类实现 hashcode 和 equals
【发布时间】:2013-07-22 00:23:44
【问题描述】:

所以我有很多自定义类,它们内部也有使用组合的自定义类。

我的自定义类的变量变化非常频繁,我将它们添加到 HashSets。所以我的问题是当我实现 hashCode - 对于只有不断变化的私有字段的类,我该怎么办?

这是一个自定义类的示例:

public class Cell {
    protected boolean isActive;
    protected boolean wasActive;

    public Cell() {
    this.isActive = false;
    this.wasActive = false;
    }

    // getter and setter methods...

    @Override
    public int hashCode() {
    // HOW SHOULD I IMPLEMENT THIS IF THIS custom object is constantly
        // being added into HashSets and have it's private fields isActive
        // and wasActive constantly changed.
    }

    // ANOTHER QUESTION Am I missing anything with this below equals implementation?
    @Override
    public boolean equals(Object object) {
    boolean result = false;
    if (object instanceof Cell) {
        Cell otherCell = (Cell) object;
        result = (this.isActive == otherCell.isActive && this.wasActive == 
            otherCell.wasActive);
    }
    return result;
    }

【问题讨论】:

  • 我猜,仅基于两个布尔变量 hashcode 不能唯一生成。您需要在类中添加一些其他变量。例如单元格的 x 和 y 坐标(仅作为示例)。我猜这不是他类的全部代码。
  • 请注意,如果您使用 Cell 作为映射中的键,那么只有这样才会产生问题,因为哈希集合不会知道更改的哈希码。因此,一旦将对象作为键添加到散列集合中,它的散列码就不能改变。您可以为单元格保留一些唯一标识符并将其用作键。
  • 这取决于您在哈希集中搜索的内容。您是在搜索特定对象,在这种情况下不实现 hashCode 或 equals,还是在搜索具有特定状态的对象,在这种情况下,您使用您感兴趣的状态变量实现 hashCode 和 equals。不断变化的字段更改不应用于 hashCode 函数,否则对象将“丢失”。
  • 为了正确实现 hashcode 和 equals 我建议你阅读 joshua 的 Effective java 中的 hashcode 和 equals 部分。他已经详细解释了这里难以解释的事情。

标签: java equals hashcode abstract-data-type custom-object


【解决方案1】:

以下是具有私有字段的类的示例。

public class Test {

    private int num;
    private String data;

    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if ((obj == null) || (obj.getClass() != this.getClass()))
            return false;
        // object must be Test at this point
        Test test = (Test) obj;
        return num == test.num
                && (data == test.data || (data != null && data
                        .equals(test.data)));
    }

    public int hashCode() {
        int hash = 7;
        hash = 31 * hash + num;
        hash = 31 * hash + (null == data ? 0 : data.hashCode());
        return hash;
    }

}

【讨论】:

  • You can't implement hascode using boolean values. 你可以,但它可能看起来不漂亮。
  • 是的,你很聪明。但是使用布尔值创建哈希码是没有用的。
【解决方案2】:

你是 SOL。在标准 Java 集合中的键中使用 hashCode() 时,它不应更改。否则你需要一个自定义的类似 HashSet 的实现。

仅使用不变的字段(或者,如果您大胆并且不介意偶尔发生崩溃,则很少更改字段)来计算 hashCode()。

(已添加)。在您的特定示例中,使用 Object.hashCode()。

(添加 #2)即使您的 Cell 类是不可变的(两个布尔值没有改变),它也是一个糟糕的散列选择,因为它只有 2 位范围。想象一下,根据他们是男性/女性和蓝眼睛/棕色眼睛来散列所有人。一个很好的开始,但只有 4 个类别,每个类别将有大约 20 亿人。理想情况下,您应该有几个其他类别,例如出生年份、出生国家/地区等。

【讨论】:

    【解决方案3】:

    Java 中的 Equals 和 hashCode 合约:

    当我们重写equals()方法时,我们必须重写hashCode(),Java中的equals方法必须遵循它与Java中hashCode方法的约定,如下所述。

    1. 如果两个对象通过 equals() 方法相等,则必须有 hashcode 一样。
    2. 如果两个对象通过equals()方法不相等,则有hashcode 可以相同也可以不同。

    这些是您的类的 equals 和 hashcode 方法的示例实现:

     //Hashcode Implementation    
    
       @Override
        public int hashCode() 
        {
            final int prime = 31;
            int result = 1;
            result = prime * result + (isActive ? 1231 : 1237);
            result = prime * result + (wasActive ? 1231 : 1237);
            return result;
        }
    
    //equals Implementation    
        @Override
        public boolean equals(Object obj) 
        {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            Cell other = (Cell) obj;
            if (isActive != other.isActive)
                return false;
            if (wasActive != other.wasActive)
                return false;
            return true;
        }
    

    【讨论】:

    • 你认为这会有什么帮助?我的意思是您在哈希码中使用可变字段。现在,如果 Cell 对象被用作散列集合中的键,并且在 HashMap 中输入时两个字段都为真,那么现在字段会发生变化,哈希码也会发生变化!如果您现在尝试根据该对象查找某些内容,则 HashMap 将在其他存储桶中查找,而不是在原始存储桶中查找,您将找不到该对象。
    • @NarendraPathai:如果具有可变字段的对象的特定实例永远不会暴露给会改变该字段的代码,那么该实例的该字段真的是可变的吗?将散列依赖于可变字段的对象存储到散列集合中的代码必须确保存储在集合中的对象不会暴露给任何可能导致其散列存储在散列集合中时发生变化的代码。通常,提供这种保证的最简单方法是使对象不可变,但在某些情况下它可能更实用......
    • ...避免将对象实例暴露给不知道存储它的散列集合的任何事物,并确保任何更改实例的请求都会从集合,然后进行更改,然后将更改后的实例添加回集合中。
    • 与其尝试实现自己的 hashCode() 方法,不如尽可能使用现有的标准实现,例如 String 的实现。为此,首先为您的对象实现 toString()。然后,可以使用 toString().equals() 构建对象的 equals() 方法,对象的 hashCode() 方法可以只返回 this.toString().hashCode()。
    猜你喜欢
    • 1970-01-01
    • 2019-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-17
    相关资源
    最近更新 更多