Java HashSet包含返回false,即使覆盖了equals()和hashCode()

Java HashSet包含返回false,即使覆盖了equals()和hashCode(),java,equals,contains,hashcode,hashset,Java,Equals,Contains,Hashcode,Hashset,我按如下方式初始化哈希集: private HashSet<Rule> ruleTable = new HashSet<Rule>(); @Override public int hashCode() { // Ignore source Port for now return (this.getSrcPool() + ":" + this.getDstPool() + ":" + this.getProtocol() + ":" + this.dstT

我按如下方式初始化哈希集:

private HashSet<Rule> ruleTable = new HashSet<Rule>();
@Override
public int hashCode() {
    // Ignore source Port for now
    return (this.getSrcPool() + ":" + this.getDstPool() + ":" + this.getProtocol() + ":" + this.dstTcp).hashCode();
}

@Override
public boolean equals(Object obj) {
    if (!(obj instanceof TcpRule))
        return false;
    if (obj == this)
        return true;

    TcpRule r = (TcpRule) obj;
    return (this.getSrcPool().equals(r.getSrcPool()) && this.getDstPool().equals(r.getDstPool()) && this.getProtocol().equals(r.getProtocol()) && this.getSrcTcp() == r.getSrcTcp() && this.getDstTcp() == r.getDstTcp());
}
我甚至编写了一个简单的单元测试,它不会给出任何错误:

@Test
public void equalsTest() {
    Pool srcPool = new Pool("PROXY");
    Pool dstPool = new Pool("WEB");
    int srcTcp = 54321;
    int dstTcp = 80;

    TcpRule r1 = new TcpRule(srcPool, dstPool, srcTcp, dstTcp);
    TcpRule r2 = r1;
    assert r1.equals(r2);

    TcpRule r3 = new TcpRule(srcPool, dstPool, srcTcp, dstTcp);
    TcpRule r4 = new TcpRule(srcPool, dstPool, srcTcp, dstTcp);
    assert r3.equals(r4);
}

@Test
public void hashCodeTest() {
    Pool srcPool = new Pool("PROXY");
    Pool dstPool = new Pool("WEB");
    int srcTcp = 54321;
    int dstTcp = 80;

    TcpRule r1 = new TcpRule(srcPool, dstPool, srcTcp, dstTcp);
    TcpRule r2 = new TcpRule(srcPool, dstPool, srcTcp, dstTcp);
    assert r1.hashCode() == r2.hashCode();

    HashSet<Rule> rules = new HashSet<Rule>();
    rules.add(r1);
    assert rules.contains(r1);

    assert rules.contains(r2);
}
在另一种方法中,我检查
哈希集中是否存在规则:

@Override
public void add(Rule rule) {
    ruleTable.add(rule);
}
    @Override
public boolean isPermittedTcp(IpAddress sourceAddress, IpAddress destinationAddress, short srcTcp, short dstTcp) {
    Pool sourcePool = poolService.getPool(new Host(sourceAddress));
    Pool destinationPool = poolService.getPool(new Host(destinationAddress));
    Rule r = new TcpRule(sourcePool, destinationPool, srcTcp, dstTcp);
    log.info("Checking: " + r.toString());
    log.info("Hash-Code: " + r.hashCode());
    log.info("Hashes in ruleTable:");
    for(Rule rT : ruleTable) {
        log.info("" + rT.hashCode());
    }
    if(ruleTable.contains(r)) {
        log.info("Hash found!");
    } else {
        log.info("Hash not found!");
    }
    return ruleTable.contains(r);
}
日志消息表明
规则
对象(
r.hashCode()
)的散列是
-1313430269
,并且
散列集
rT.hashCode()
在循环中)中的一个散列也是
-1313430269
。 但是
ruleTable.contains(r)
总是返回
false
。我做错了什么


我在StackOverflow上发现了类似的问题,但这些问题主要涉及未(正确)覆盖的
equals()
hashCode()
方法。我想我已经正确地实现了这两种方法

在equals this.getSrcTcp()==r.getSrcTcp()中有一个额外的条件,它不是哈希代码的一部分-也许这就是问题所在,哈希代码是相同的,但equals是false。检查此字段与所比较的值是否不同

尽管有评论,我认为这不起作用的原因是equals&hashCode实现没有使用相同的字段

模拟问题的代码:

import java.util.HashSet;

/**
 * @author u332046
 *
 */
public class HashCodeCollisionTest {
    public static class KeyDemo {
        String id;

        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + ((id == null) ? 0 : id.hashCode());
            return result;
        }

        @Override
        public boolean equals(Object obj) {
            /*if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            KeyDemo other = (KeyDemo) obj;
            if (id == null) {
                if (other.id != null)
                    return false;
            } else if (!id.equals(other.id))
                return false;
            return true;*/
            return false;
        }

        public KeyDemo(String id) {
            super();
            this.id = id;
        }
    }

    static HashSet<KeyDemo> set = new HashSet<>();

    public static void main(String[] args) {
        set.add(new KeyDemo("hi"));
        set.add(new KeyDemo("hello"));

        System.out.println(set.contains(new KeyDemo("hi")));
    }
}
import java.util.HashSet;
/**
*@作者u332046
*
*/
公共类HashCodeCollisionTest{
公共静态类KeyDemo{
字符串id;
@凌驾
公共int hashCode(){
最终整数素数=31;
int结果=1;
result=prime*result+((id==null)?0:id.hashCode();
返回结果;
}
@凌驾
公共布尔等于(对象obj){
/*if(this==obj)
返回true;
if(obj==null)
返回false;
如果(getClass()!=obj.getClass())
返回false;
KeyDemo其他=(KeyDemo)对象;
if(id==null){
if(other.id!=null)
返回false;
}如果(!id.equals(other.id))
返回false;
返回true*/
返回false;
}
公钥演示(字符串id){
超级();
this.id=id;
}
}
静态HashSet=newhashset();
公共静态void main(字符串[]args){
添加(新的KeyDemo(“hi”);
添加(新的KeyDemo(“hello”);
System.out.println(set.contains(新的KeyDemo(“hi”));
}
}

这将打印
false
。取消对equals代码的注释,它将打印
true

您的问题是
hashCode()
equals()
不一致

您的
hashCode()
实现基于池的
toString()
,但是您的
equals()
使用池类的
.equals()


更改
.equals()
以比较用于生成哈希代码的字符串。

有一些可能性:

  • 规则
    是可变的,在将规则添加到set后,某个键(w.r.t.散列或等于)字段被更改
  • 如果两个对象相等,它们应该具有相同的哈希代码
  • Bug,比如使用
    ==
    i.o.
    equals
    equals
    中进行比较

在这里,我想您有两个
Pool
实例,在Pool name上没有equals,在Pool name上没有hashCode。

并且rT.equals(r)吗?您的测试使用相同的Pool实例,您确定equals在Pool上工作得很好吗?在equals this中有一个额外的条件。getSrcTcp()==r.getSrcTcp()这不是散列代码的一部分-也许这就是问题所在,散列代码是相同的,但等于是错误的。如果您发布一个简短但完整的示例来演示这个问题,这将非常有帮助……顺便说一句,一旦您解决了问题,您可能需要修改您实现
hashcode()
方法的方式。连接字符串以计算哈希代码的成本比需要的更高。阅读其他观点。你还没有证明你认为你已经证明的东西。您的
equals
方法总是返回
false
,因此显然它永远不会打印“true”。更好的测试方法是取消注释
equals
中的所有代码,但使
hashCode()
返回0-因此所有实例的值都相同。另外,请阅读
对象的文档。hashCode
:“如果两个对象根据equals(java.lang.Object)不相等,则不需要这样做。”方法,然后对两个对象中的每一个调用hashCode方法必须生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。”我试图指出,两者都需要一致。HashMap.getEntry确实检查等式
如果(e.hash==hash&&((k=e.key)==key | | |(key!=null&&key.equals(k)))
我觉得这很有趣,因为你是对的,也是错的。你错了,
equals
hashCode
都需要基于完全相同的字段列表进行计算(像往常一样,@Jon Skeet在右边是100%)。然而,很可能你是对的,不同的领域是导致OP不相信的原因。但这并不是因为HashSet做了错事。这可能是因为OP做出了错误的假设,即拥有相等的
hashcode
自动意味着
HashSet.Contains()
检查将返回true。@6ton:假设您有一个字段,其中计算一个合适的哈希代码可能非常昂贵,但平等性检查通常很便宜,而且实例之间的差异很小,仅限于该字段。在这种情况下,不将其包含在哈希代码中是有意义的。