自定义equals()方法无法正常工作

时间:2014-11-30 10:33:46

标签: java map override equals hashset

我已经实现了Apriori算法。它工作得很好,但我遇到了一个奇怪的问题:我已经定义了一个Rule类来维护生成的规则。

这是:

public class Rule 
{
    private Set<Integer> left;
    private Set<Integer> right;
    private LookupArtist lookupArtist;

    public Rule(LookupArtist lookupArtist){
        left = new HashSet<>();
        right = new HashSet<>();
        this.lookupArtist = lookupArtist;
    }

    @Override
    public boolean equals(Object another){
        Rule rule = (Rule) another;

        if(this.left.equals(rule.getLeft()) && this.right.equals(rule.getRight()))
            return true;
        else
            return false;
    }

    @Override
    public String toString(){
        /* print the object */
    }

    public void addToLeft(Integer toAdd){
        left.add(toAdd);
    }

    public void addToRight(Integer toAdd){
        right.add(toAdd);
    }

    public Set<Integer> getLeft(){
        return left;
    }

    public Set<Integer> getRight(){
        return right;
    }

}

我还尝试以不同的方式实现equals()方法:

@Override
    public boolean equals(Object another){
        Rule rule = (Rule) another;
        boolean flag = true;
        for(Integer artist : left){
            if(flag)
            if(!rule.left.contains(artist))
                flag=false;
        }

        if(flag)
            for(Integer artist : right){
                if(flag)
                if(!rule.right.contains(artist))
                    flag=false;
            }
        return flag;

    }

LookupArtist对象用于将整数映射到某些字符串。

问题在于,当我打印出规则时,我发现有些规则会出现两次。我还在调试模式下找到了一些复制规则,因此它不是打印问题。规则保存在这样的地图中:

static Map<Rule, Float> rules;

.
.
.

Rule rule = new Rule(lookupArtist);
for(int j=0;j<i;j++){
    rule.addToLeft(a[j]);
}
for(int j=i;j<a.length;j++){
    rule.addToRight(a[j]);
}
if(!rules.containsKey(rule)){
    rules.put(rule, getRuleConfidence(rule));
}

知道问题出在哪里?

3 个答案:

答案 0 :(得分:3)

使用HashSet存储具有自定义equals实现的类的对象时,您必须具有匹配的hashCode自定义实现。

如果两个对象相等(根据自定义equals实现), they must have the same hashCode . In the code you posted, I don't see an overriding of hashCode in the Rule`类。

将实例添加到HashSet时,hashCode方法用于确定将在其中存储实例的哈希表中的索引。然后,迭代存储在此索引中的实例的链接列表,以查看该实例是否已存在。迭代该列表时,使用equals。如果两个相等的对象由hashCode映射到HashSet中的不同索引,则不会检测到复制,因为它们将存储在单独的链接列表中。

这在equals

的Javadoc中有说明
 * Note that it is generally necessary to override the <tt>hashCode</tt>
 * method whenever this method is overridden, so as to maintain the
 * general contract for the <tt>hashCode</tt> method, which states
 * that equal objects must have equal hash codes. 

hashCode的Javadoc中:

 * <li>If two objects are equal according to the <tt>equals(Object)</tt>
 *     method, then calling the <code>hashCode</code> method on each of 
 *     the two objects must produce the same integer result. 

答案 1 :(得分:1)

你的hashCode()方法在哪里?这也很重要:)

答案 2 :(得分:1)

覆盖hashCode时,您应始终覆盖equals,反之亦然。

在您的Rule课程中添加以下内容:

@Override
public int hashCode() {
    return left.hashCode()
         ^ right.hashCode()
         ^ lookupArtist.hashCode();
}

Here是一个很好的答案,解释了为什么重写两者都很重要。

此外,您的equals方法可以写为

@Override
public boolean equals(Object another){
    Rule rule = (Rule) another;
    return left.equals(rule.left)
        && right.equals(rule.right)
        && lookupArtist.equals(rule.lookupArtist);
}

最后一句话:你对equals-implementation的另一种尝试不是对称的,即rule1.equals(rule2)当{且仅当rule2.equals(rule1)时不是这种情况。这违反了平等合同。