簡體   English   中英

hashCode實現“等於任何一些字段相等”

[英]hashCode implementation for “equals any of some fields are equal”

如果其中一個字段相等,我希望特定類的對象相等。 如何為這樣的類編寫一致的hashCode方法?

(免責聲明因為我知道這不是最佳實踐:該類是另一個類的包裝器,應該用於Maps中的鍵。這兩個不同的具有一個相等字段的對象將導致相同的Map條目。實際上每個字段都會自己識別底層對象,但我並不總是對兩個可用的對象具有相同的識別字段。我無法控制並因此改變這種“模糊”識別機制。也可以采用替代解決方案來解決這個問題。)

是否有相應的實現hashCode()的策略? 我只知道在equals中涉及連詞(和&&)的實現。 如果其中一個字段相等,如何確保hashCode()是相同的?

這是我想要編寫一致的hashCode()實現的簡化equals方法:

public boolean equals(C other)
{
    return (field1 != null && field1.equals(other.field1))
            || (field2 != null && field2.equals(other.field2))
            || (field3 != null && field3.equals(other.field3));
}

編輯:根據輸入數據,沒有像(1,2,3)等情況等於(1,6,7)。 只生成對象,使得某些字段可以為null,但不會像示例中那樣自相矛盾。 簡單地說,實際上只有(1,2,3)的組合應該是(1,2,3),(1,null,null),(null,2,null),(1,2,null)和等等。 我承認這種方法並不是特別強大。

您通常不使用對象類的一個字段來實現equals()hashCode() 每個人都可能會建議你反對它。 通常的做法是確保比較所有字段並確保它們都相等,以便調用.equals() hashCode()使用.equals()來散列這些對象。 但是,如果你可以控制你正在做的事情,你可以簡單地使用你對象的特定字段的hashCode() ()並基於它覆蓋.equals()和.hashCode() (但同樣不可取)。

您不能使用任何字段來實現hashCode因為該字段始終不等於。

您的hashCode方法需要始終為equals對象返回相同的值。 由於在equals方法中只需要一個字段相同,並且它並不總是相同,因此您唯一的選擇是在hashCode方法中返回一個常量。

實現效率低下但是有效且與equals一致。

實施可能是:

public int hashCode() {
    return 0;
}

似乎唯一的解決方案就是這個

public int hashCode() {
    return 1;
}

在問題中實現equals()

  public boolean equals(C other) {
    //TODO: you have to check if other != null...

    return (field1 != null && field1.equals(other.field1)) ||
           (field2 != null && field2.equals(other.field2)) ||
           (field3 != null && field3.equals(other.field3));
  }

不正確的 實施平等時,我們必須確保這一點

  a.equals(a)
  if a.equals(b) then b.equals(a)
  if a.equals(b) and b.equals(c) then a.equals(c)

第一個規則的計數器示例所有比較字段field1, field2, field3 )為null的實例:

  MyObject a = new MyObject();
  a.setField1(null);
  a.setField2(null);
  a.setField3(null);

  a.equals(a); // <- return false

3d規則的反例

  MyObject a = new MyObject();
  a.setField1("1"); // field2 and field3 == null 

  MyObject b = new MyObject();
  b.setField1("1"); // field3 == null 
  b.setField2("2"); 

  MyObject c = new MyObject();
  c.setField2("2");  // field1 and field3 == null

  a.equals(b); // true (by field1)
  b.equals(c); // true (by field2)
  a.equals(c); // false!

這就是為什么沒有 hashCode() 解決方案 ......

從你說它不是一個好習慣的角度開始,我認為你可以實現這一點的一種方法是維護對每個對象中另一個對象的引用,並根據字段的相等性計算hashCode:

public class Test {
    private String field1;
    private Integer field2;

    private Test other;

    public String getField1() {
        return field1;
    }

    public void setField1(String field1) {
        this.field1 = field1;
    }

    public int getField2() {
        return field2;
    }

    public void setField2(int field2) {
        this.field2 = field2;
    }

    public Test getOther() {
        return other;
    }

    public void setOther(Test other) {
        this.other = other;
    }

    @Override
    public int hashCode() {
        if (other == null) {
            return super.hashCode();
        }

        int hashCode = 1;
        if (field1 != null && field1.equals(other.field1)) {
            hashCode = 31 * hashCode + (field1 == null ? 0 : field1.hashCode());
        }
        if (field2 != null && field2.equals(other.field2)) {
            hashCode = 31 * hashCode + field2.hashCode();
        }
        if (hashCode == 1) {
            hashCode = super.hashCode();
        }
        return hashCode;
    }

    public boolean equals(Test other) {
        return (field1 != null && field1.equals(other.field1))
                || (field2 != null && field2.equals(other.field2));
    }

    public static void main(String[] args) {
        Test t1 = new Test();
        t1.setField1("a");
        t1.setField2(1);

        Test t2 = new Test();
        t2.setField1("a");
        t2.setField2(1);

        t1.setOther(t2);
        t2.setOther(t1);

        System.out.println("Equals: " + t1.equals(t2));
        System.out.println("Hash 1: " + t1.hashCode());
        System.out.println("Hash 2: " + t2.hashCode());

        t2.setField2(2);
        System.out.println("Equals: " + t1.equals(t2));
        System.out.println("Hash 1: " + t1.hashCode());
        System.out.println("Hash 2: " + t2.hashCode());

        t2.setField1("b");
        System.out.println("Equals: " + t1.equals(t2));
        System.out.println("Hash 1: " + t1.hashCode());
        System.out.println("Hash 2: " + t2.hashCode());
    }
}

解決這個問題的替代解決方案也是受歡迎的。

我永遠不會搞亂equals()和hashCode()。 只需為對象正確實現它們即可。 編寫滿足您需求的自定義比較器,並使用支持自定義比較器(例如TreeSetTreeMap )的集合來執行查找。

樣品比較器:

public class SingleFieldMatchComparator implements Comparator<Key> {

  public int compare(Key key1, Key key2) {
    if (key1 == null) {
      if (key2 == null) {
        return 0;
      }
      else {
        return 1;
      }
    } else if (key2 == null) {
      return -1;
    }

    int result = ObjectUtils.compare(key1.getField1(), key2.getField1());
    if (result == 0) {
      return 0;
    }

    result = ObjectUtils.compare(key1.getField2(), key2.getField2());
    if (result == 0) {
      return 0;
    }

    return ObjectUtils.compare(key1.getField3(), key2.getField3());
  }

}

注意:上面使用ObjectUtils來減少代碼。 如果依賴項不值得,可以用私有方法替換。

示例程序:

Map<Key,Key> myMap = new TreeMap<Key,Key>(new SingleFieldMatchComparator());
Key key = new Key(1, 2, 3);
myMap.put(key, key);
key = new Key(3, 1, 2);
myMap.put(key, key);

System.out.println(myMap.get(new Key(null, null, null)));
System.out.println(myMap.get(new Key(1, null, null)));
System.out.println(myMap.get(new Key(null, 2, null)));
System.out.println(myMap.get(new Key(null, null, 2)));
System.out.println(myMap.get(new Key(2, null, null)));

輸出:

null
1,2,3
1,2,3
3,1,2
null

暫無
暫無

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

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