简体   繁体   English

hashCode实现“等于任何一些字段相等”

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

I want objects of a particular class to be equal if one of their fields are equal. 如果其中一个字段相等,我希望特定类的对象相等。 How can I write a consistent hashCode method for such a class? 如何为这样的类编写一致的hashCode方法?

(Disclaimer because I know this is not best practice: The class is a wrapper for another class and should be used for keys in Maps. It is intended that two different of these objects with one equal field would result in the same Map entry. Actually each of the fields would identify the underlying object on their own but I do not always have the same identifying field for two objects available. I cannot control and therefore change this "ambiguous" identification mechanism. Alternative solutions to tackle this are also welcome.) (免责声明因为我知道这不是最佳实践:该类是另一个类的包装器,应该用于Maps中的键。这两个不同的具有一个相等字段的对象将导致相同的Map条目。实际上每个字段都会自己识别底层对象,但我并不总是对两个可用的对象具有相同的识别字段。我无法控制并因此改变这种“模糊”识别机制。也可以采用替代解决方案来解决这个问题。)

Are there strategies for implementing hashCode() accordingly? 是否有相应的实现hashCode()的策略? I only know of implementations involving conjunctions (as with &&) in equals. 我只知道在equals中涉及连词(和&&)的实现。 How to make sure that hashCode() is the same if either of the fields is equal? 如果其中一个字段相等,如何确保hashCode()是相同的?

Here is the simplified equals method for which I would like to write a consistent hashCode() implementation: 这是我想要编写一致的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));
}

EDIT: as per the input data, no cases like (1, 2, 3) equals (1, 6, 7) can occur. 编辑:根据输入数据,没有像(1,2,3)等情况等于(1,6,7)。 The objects are only produced such that some of the fields can be null, but not contradicting as in the example. 只生成对象,使得某些字段可以为null,但不会像示例中那样自相矛盾。 Simply put in practise the only combinations equal to (1, 2, 3) should be (1, 2, 3), (1, null, null), (null, 2, null), (1, 2, null) and so forth. 简单地说,实际上只有(1,2,3)的组合应该是(1,2,3),(1,null,null),(null,2,null),(1,2,null)和等等。 I acknowledge that this approach is not particularly robust. 我承认这种方法并不是特别强大。

You don't usually implement equals() and hashCode() using just one field of your object class. 您通常不使用对象类的一个字段来实现equals()hashCode() Everyone will probably advise you against it. 每个人都可能会建议你反对它。 The general practice is to ensure that you compare all the fields and ensure that they are all equal in order to call .equals() . 通常的做法是确保比较所有字段并确保它们都相等,以便调用.equals() hashCode() uses .equals() in order to hash those objects. hashCode()使用.equals()来散列这些对象。 However, if you can control what you are doing, you can simply use the hashCode() of a particular field of your object and override .equals() and .hashCode() based on that (but again, not advisable). 但是,如果你可以控制你正在做的事情,你可以简单地使用你对象的特定字段的hashCode() ()并基于它覆盖.equals()和.hashCode() (但同样不可取)。

You can't use any fields to implement the hashCode because the field aren't equals all the time. 您不能使用任何字段来实现hashCode因为该字段始终不等于。

Your hashCode method needs to return the same value for equals objects all the time. 您的hashCode方法需要始终为equals对象返回相同的值。 As only one field needs to be equal in your equals method, and it isn't always the same, your only option is to return a constant in your hashCode method. 由于在equals方法中只需要一个字段相同,并且它并不总是相同,因此您唯一的选择是在hashCode方法中返回一个常量。

The implementation is inefficient but it is valid and consistent with equals. 实现效率低下但是有效且与equals一致。

The implementation could be: 实施可能是:

public int hashCode() {
    return 0;
}

It seems the only solution is this 似乎唯一的解决方案就是这个

public int hashCode() {
    return 1;
}

The implementation of equals() in the question 在问题中实现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));
  }

is incorrect one . 不正确的 When implementing equals we have to ensure that 实施平等时,我们必须确保这一点

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

Counter example for the 1st rule is the instance where all fields of comparison ( field1, field2, field3 ) are null : 第一个规则的计数器示例所有比较字段field1, field2, field3 )为null的实例:

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

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

Counter example for the 3d rule: 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!

And that's why there's no solution for hashCode() ... 这就是为什么没有 hashCode() 解决方案 ......

Starting from the point that as you say it is not a good practice, I think one way you can achieve this is maintanting a reference to the other object in each of the objects and computing hashCode based on the equality of the fields: 从你说它不是一个好习惯的角度开始,我认为你可以实现这一点的一种方法是维护对每个对象中另一个对象的引用,并根据字段的相等性计算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());
    }
}

Alternative solutions to tackle this are also welcome. 解决这个问题的替代解决方案也是受欢迎的。

I would never mess with equals() & hashCode(). 我永远不会搞乱equals()和hashCode()。 Simply implement them correctly for the objects. 只需为对象正确实现它们即可。 Write a custom comparator meeting your needs and use a collection that supports a custom comparator (eg TreeSet or TreeMap ) to perform your lookup. 编写满足您需求的自定义比较器,并使用支持自定义比较器(例如TreeSetTreeMap )的集合来执行查找。

Sample comparator: 样品比较器:

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

}

Note: above uses ObjectUtils to reduce the code a bit. 注意:上面使用ObjectUtils来减少代码。 Can be replaced with a private method if the dependency isn't worth it. 如果依赖项不值得,可以用私有方法替换。

Sample program: 示例程序:

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

Output: 输出:

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