Java “的哈希代码实现”;等于某些字段中的任何字段都是相等的“;

Java “的哈希代码实现”;等于某些字段中的任何字段都是相等的“;,java,equals,hashcode,Java,Equals,Hashcode,如果某个类的某个字段相等,我希望该类的对象相等。如何为这样的类编写一致的hashCode方法 (免责声明,因为我知道这不是最佳实践:该类是另一个类的包装器,应用于映射中的键。其目的是使用一个相等字段的两个不同对象将导致相同的映射条目。实际上,每个字段都将自己标识基础对象,但我并不总是具有sa两个对象的me标识字段可用。我无法控制并因此更改此“模糊”标识机制。也欢迎解决此问题的其他解决方案。) 有没有相应地实现hashCode()的策略?我只知道在equals中包含连词(如&&)的实现。如果其中一

如果某个类的某个字段相等,我希望该类的对象相等。如何为这样的类编写一致的hashCode方法

(免责声明,因为我知道这不是最佳实践:该类是另一个类的包装器,应用于映射中的键。其目的是使用一个相等字段的两个不同对象将导致相同的映射条目。实际上,每个字段都将自己标识基础对象,但我并不总是具有sa两个对象的me标识字段可用。我无法控制并因此更改此“模糊”标识机制。也欢迎解决此问题的其他解决方案。)

有没有相应地实现hashCode()的策略?我只知道在equals中包含连词(如&&)的实现。如果其中一个字段相等,如何确保hashCode()是相同的

下面是简化的equals方法,我想为其编写一个一致的hashCode()实现:

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)这样的情况。对象的生成只是为了使某些字段可以为空,但不与示例中的字段相矛盾。只需实践一下,等于(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));
  }
是不正确的。在实施equals时,我们必须确保

  a.equals(a)
  if a.equals(b) then b.equals(a)
  if a.equals(b) and b.equals(c) then a.equals(c)
第1条规则的反例是所有字段 比较值(
field1、field2、field3
)为
null

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

  a.equals(a); // <- return 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()。只需为对象正确实现它们。编写一个满足您需要的自定义比较器,并使用支持自定义比较器(例如or)的集合来执行查找

样本比较器:

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

}
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

因此,仅在hashcode实现中使用该字段。该相等关系是不可传递的。无论如何,您都将违反约定。存在多个字段,如果其中任何字段(成对)在两个实例中相等,则这两个实例将被视为“相等”有几种方法可以做到这一点,但您应该查看覆盖equals()和hashCode()的契约方法实现。你为什么需要这个奇怪的
equals
方法呢?你不能定义一个实现这个逻辑的函数对象,并且只在你真正需要它的地方显式地使用它。它会使你的代码更干净,避免将来出现许多混乱的错误。OP可能会问“是否只有一个字段”可用于实现equals()和hashCode()。@ha9u63ar-在这种情况下,答案是否定的。equals()可以在字段3上返回true,但hasCode()可能是基于字段1生成的。@ha9u63ar谢谢,我误解了这个问题,我更新了我的答案。事实上,我还可以想出2^32个解决方案…2^32+1)st solution正在抛出异常。@Prashant-这是正确的解决方案。
等式
契约本身有缺陷,不要期望'hashCode()`没有缺陷。这确实可行,但它也会退化哈希映射,从而降低性能,不是吗?如果您将
equals
实现为非等价关系,Java集合框架无论如何都会退出。@我得到的最后一点:Dyou是对的,但这种情况在实践中不会发生,因为对象是如何实现的将从输入数据创建(不幸的是,我不能详细说明用例,我可能稍后会尝试澄清这个问题,但似乎我更应该寻找一个更好的方法)@JayK:您的实现也违反了3d规则(请参见我的编辑)你是对的,我还没有考虑过这种情况。如果我回到这个方法,我将不得不评估它与实际输入和周围算法的关系,并在必要时考虑适当的“修复”。
null
1,2,3
1,2,3
3,1,2
null