Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java HashSet contains returns false, even with overridden equals() and hashCode()

I initialize the HashSet like this:

private HashSet<Rule> ruleTable = new HashSet<Rule>();

The equals() and hashCode() methods of my TcpRule object (sub-class of abstract class Rule) look like this:

@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());
}

I have even written a simple unit test, which does not give any error:

@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);
}

In my application, I have an add() method where I simply add a Rule object to the HashSet:

@Override
public void add(Rule rule) {
    ruleTable.add(rule);
}

In another method, I check if a rule exists in the HashSet:

    @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);
}

The log messages indicate that the hash of the Rule object (r.hashCode()) is -1313430269, and that one hash in the HashSet (rT.hashCode() in the loop) is also -1313430269. But ruleTable.contains(r) always returns false. What am I doing wrong?

I have found similar questions on StackOverflow, but these mostly involve the equals() or hashCode() methods not being (correctly) overridden. I think I have implemented this two methods correctly.

like image 654
NoBodyIsPerfect Avatar asked Nov 21 '22 12:11

NoBodyIsPerfect


1 Answers

Your problem is that hashCode() and equals() do not agree.

Your hashCode() implementation is based on the toString() of the pool, but your equals() uses .equals() of the pool class.

Change your .equals() to compare the strings used to generate the hash code.

like image 95
Bohemian Avatar answered Nov 30 '22 23:11

Bohemian