来自一个字段的 Java hashCode

发布于 2024-09-15 21:27:52 字数 262 浏览 4 评论 0 原文

编辑:准备我的对象以在 HashMap 中使用。

在阅读了一些有关如何生成哈希码的信息后,我现在有点困惑。我的(可能是微不足道的)问题是,当我有一个可以使用的字段时,我应该如何实现 hashCode 方法?我可以直接使用字段吗? 如果我理解正确,则 hashCode 的值在对象的生命周期内不得更改,并且我只有一个适合此的 ID 归档,但我在其他地方读过,不应该使用 ID...尽管如此,如何基于这个(唯一且不变)值的 hashCode 函数会是什么样子? equals方法也是仅基于id..

Edit: Prepare my objects for the use within a HashMap.

after reading a bit about how to generate a hash code, im kind of confused now. My (probably trivial) question is, how should I implement a hashCode method when I have one field that I could use? Can I use the fiels directly?
If I understand correctly, the values for hashCode must not change during the lifetime of an object, and I only have an ID filed that fits this, but I have read otherwhere, that one should not use ID...despide of that, how would a hashCode function based on this one (unique and not changing) value look like? The equals method is also based on the id only..

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

心头的小情儿 2024-09-22 21:27:52

如果您的对象是可变的,那么其哈希码随时间变化是可以接受的。当然,您应该更喜欢不可变对象(Effective Java 第 2 版,第 15 项:最小化可变性)。

以下是 Josh Bloch 的哈希码配方,来自 Effective Java 第 2 版,第 9 项:当您覆盖 equals 时,始终覆盖 hashCode

Effective Java 第 2 版哈希码配方

  • 将一些常量非零值(例如 17)存储在名为 resultint 变量中。
  • 计算每个字段的 int 哈希码 c
    • 如果该字段是布尔值,则计算(f ? 1 : 0)
    • 如果字段是byte、char、short、int,则计算(int) f
    • 如果字段为long,则计算(int) (f ^ (f >>> 32))
    • 如果字段是float,则计算Float.floatToIntBits(f)
    • 如果字段是 double,则计算 Double.doubleToLongBits(f),然后对结果 long 进行哈希处理,如上所示。李>
    • 如果该字段是对象引用,并且此类的 equals 方法通过递归调用 equals 来比较该字段,则对该字段递归调用 hashCode 。如果该字段的值为null,则返回0。
    • 如果该字段是一个数组,则将其视为每个元素都是一个单独的字段。如果数组字段中的每个元素都很重要,您可以使用版本 1.5 中添加的 Arrays.hashCode 方法之一。
  • 将哈希码 c 组合成 result,如下所示: result = 31 * result + c;

按原样遵循配方是正确的,即使只有一个字段。只需根据字段的类型执行适当的操作即可。

请注意,有些库实际上可以为您简化此操作,例如 Apache Commons Lang 的“noreferrer">HashCodeBuilder,或者只是来自 >java.util.Arrays

这些库允许您简单地编写如下内容:

@Override public int hashCode() {
    return Arrays.hashCode(new Object[] {
        field1, field2, field3, //...
    });
}

Apache Commons Lang 示例

这是使用 Apache Commons Lang 的构建器来促进方便且可读的 equalshashCodetoStringcompareTo

import org.apache.commons.lang.builder.*;

public class CustomType implements Comparable<CustomType> {
    // constructors, etc
    // let's say that the "significant" fields are field1, field2, field3
    @Override public String toString() {
        return new ToStringBuilder(this)
            .append("field1", field1)
            .append("field2", field2)
            .append("field3", field3)
                .toString();
    }
    @Override public boolean equals(Object o) {
        if (o == this) { return true; }
        if (!(o instanceof CustomType)) { return false; }
        CustomType other = (CustomType) o;
        return new EqualsBuilder()
            .append(this.field1, other.field1)
            .append(this.field2, other.field2)
            .append(this.field3, other.field3)
                .isEquals();
    }
    @Override public int hashCode() {
        return new HashCodeBuilder(17, 37)
            .append(field1)
            .append(field2)
            .append(field3)
                .toHashCode();
    }
    @Override public int compareTo(CustomType other) {
        return new CompareToBuilder()
            .append(this.field1, other.field1)
            .append(this.field2, other.field2)
            .append(this.field3, other.field3)
                .toComparison();
    }
}

这四种方法编写起来非常繁琐,并且很难确保遵守所有约定,但幸运的是库至少可以帮助让工作变得更容易。一些 IDE(例如 Eclipse)还可以自动为您生成其中一些方法。

另请参阅

If your object is mutable, then it is acceptable to have its hash code change over time. Of course, you should prefer immutable objects (Effective Java 2nd Edition, Item 15: Minimize mutability).

Here's the hashcode recipe from Josh Bloch, from Effective Java 2nd Edition, Item 9: Always override hashCode when you override equals:

Effective Java 2nd Edition hash code recipe

  • Store some constant nonzero value, say 17, in an int variable called result.
  • Compute an int hashcode c for each field:
    • If the field is a boolean, compute (f ? 1 : 0)
    • If the field is a byte, char, short, int, compute (int) f
    • If the field is a long, compute (int) (f ^ (f >>> 32))
    • If the field is a float, compute Float.floatToIntBits(f)
    • If the field is a double, compute Double.doubleToLongBits(f), then hash the resulting long as in above.
    • If the field is an object reference and this class's equals method compares the field by recursively invoking equals, recursively invoke hashCode on the field. If the value of the field is null, return 0.
    • If the field is an array, treat it as if each element is a separate field. If every element in an array field is significant, you can use one of the Arrays.hashCode methods added in release 1.5.
  • Combine the hashcode c into result as follows: result = 31 * result + c;

It would be correct to follow the recipe as is, even with just one field. Just do the appropriate action depending on the type of the field.

Note that there are libraries that actually simplify this for you, e.g. HashCodeBuilder from Apache Commons Lang, or just Arrays.hashCode/deepHashCode from java.util.Arrays.

These libraries allows you to simply write something like this:

@Override public int hashCode() {
    return Arrays.hashCode(new Object[] {
        field1, field2, field3, //...
    });
}

Apache Commons Lang example

Here's a more complete example of using the builders from Apache Commons Lang to facilitate a convenient and readable equals, hashCode, toString, and compareTo:

import org.apache.commons.lang.builder.*;

public class CustomType implements Comparable<CustomType> {
    // constructors, etc
    // let's say that the "significant" fields are field1, field2, field3
    @Override public String toString() {
        return new ToStringBuilder(this)
            .append("field1", field1)
            .append("field2", field2)
            .append("field3", field3)
                .toString();
    }
    @Override public boolean equals(Object o) {
        if (o == this) { return true; }
        if (!(o instanceof CustomType)) { return false; }
        CustomType other = (CustomType) o;
        return new EqualsBuilder()
            .append(this.field1, other.field1)
            .append(this.field2, other.field2)
            .append(this.field3, other.field3)
                .isEquals();
    }
    @Override public int hashCode() {
        return new HashCodeBuilder(17, 37)
            .append(field1)
            .append(field2)
            .append(field3)
                .toHashCode();
    }
    @Override public int compareTo(CustomType other) {
        return new CompareToBuilder()
            .append(this.field1, other.field1)
            .append(this.field2, other.field2)
            .append(this.field3, other.field3)
                .toComparison();
    }
}

These four methods can be notoriously tedious to write, and it can be difficult to ensure that all of the contracts are adhered to, but fortunately libraries can at least help make the job easier. Some IDEs (e.g. Eclipse) can also automatically generate some of these methods for you.

See also

夜血缘 2024-09-22 21:27:52

如果您希望具有不同 id 的对象由该 id 标识,您需要做的就是返回它/比较它。

private final int id;

public int hashCode() { return id; }

public boolean equals(Object o) { 
    return o instanceof ThisClass && id == ((ThisClass)o).id;
}

If you want objects with different ids to identified by that id all you need to do is return it/compare it.

private final int id;

public int hashCode() { return id; }

public boolean equals(Object o) { 
    return o instanceof ThisClass && id == ((ThisClass)o).id;
}
寂寞笑我太脆弱 2024-09-22 21:27:52

用多少个字段来计算hashCode并不重要。
但使用equals()很重要。如果A等于B,则它们的hashCode必须相同。

如果你讨厌 hashCode:) 并且你的对象永远不会被放入基于哈希的容器(HashMap、HashSet ..),只需保留 hashCode() ,让其基类计算 hashCode 即可。

It does not matter that how many fields are used to calculate hashCode.
But it matters working with equals(). If A equals B, their hashCode must be same.

If you hate hashCode:) and your object never be put to hash based containers(HashMap, HashSet..), just leave hashCode() alone, let its base class to calculate hashCode.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文