簡體   English   中英

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

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

我像這樣初始化 HashSet:

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

我的TcpRule對象(抽象類Rule子類equals()equals()hashCode()方法如下所示:

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

在我的應用程序中,我有一個add()方法,我只需將一個Rule對象添加到HashSet

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

在另一種方法中,我檢查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);
}

日志消息表明Rule對象 ( r.hashCode() ) 的哈希值為-1313430269 ,並且HashSet中的一個哈希值rT.hashCode()循環中的rT.hashCode() )也是-1313430269 但是ruleTable.contains(r)總是返回false 我究竟做錯了什么?

我在 StackOverflow 上發現了類似的問題,但這些問題主要涉及未(正確)覆蓋的equals()hashCode()方法。 我想我已經正確地實現了這兩種方法。

你的問題是, hashCode()equals()同意。

您的hashCode()實現基於池的toString() ,但您的equals()使用池類的.equals()

更改您的.equals()以比較用於生成哈希碼的字符串。

有一些可能性:

  • Rule是可變的,在向集合中添加規則后,某些鍵(wrt 哈希或等於)字段發生了變化;
  • 如果兩個對象相等,則它們應該具有相同的 hashCode;
  • 錯誤,例如使用== io equals進行equals比較。

在這里,我猜你有兩個Pool實例,在池名稱上沒有 equals 或在池名稱上沒有 hashCode。

你在 equals this.getSrcTcp() == r.getSrcTcp() 中有一個額外的條件,它不是哈希碼的一部分——也許這就是問題所在,哈希碼是相同的,但等於是假的。 檢查此字段在您比較的值中是否不同。

盡管有評論,我認為這不起作用的原因是 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")));
    }
}

這會打印false 取消注釋 equals 代碼並打印true

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM