Java 理想的哈希代码实现

Java 理想的哈希代码实现,java,eclipse,equals,hashcode,Java,Eclipse,Equals,Hashcode,我的课程如下 public class Hash { int age; int id; String name; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; result = prime * result + ((name == nul

我的课程如下

public class Hash {
    int age;
    int id;
    String name;
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + id;
        result = prime * result + ((name == null) ? 0 : name.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;
        Hash other = (Hash) obj;
        if (id != other.id)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        return true;
    }


}
在本文中,我只使用了两个属性name和id来生成.equals方法,但现在我的问题是。实现.hashCode()方法的好方法是什么

  • 仅使用名称和id
  • 使用所有三个名称、年龄和id

  • 两者的优点和缺点是什么?

    不用想太多,我会使用:apache的

    要使用此类,请按如下方式编写代码:

     public class Person {
       String name;
       int age;
       boolean smoker;
       ...
    
       public int hashCode() {
         // you pick a hard-coded, randomly chosen, non-zero, odd number
         // ideally different for each class
         return new HashCodeBuilder(17, 37).
           append(name).
           append(age).
           append(smoker).
           toHashCode();
       }
     }
    
    带答案:是否只有
    名称
    id
    唯一地标识类的每个对象,并且与
    相等()
    进行比较时一致?如果,那么为什么我应该使用三个属性而不是两个属性


    ,有点旧,但细节不错。

    计算hashcode的一条重要规则是,您必须只使用与
    equals()
    实现相关的信息

    由于在
    equals
    方法中没有使用
    age
    ,因此在
    hashCode
    中使用它是错误的!道理很简单:

    • 想象两个对象o1和o2的属性为“id=1,age=2,name=foo”和“id=1,age=3,name=foo”
    • 调用
      equals()
      将返回
      true
    • 但是,调用
      hashCode()
      (在使用
      age
      的实现上)将返回两个不同的值
    这并不意味着你必须使用所有这些信息。有时,只使用一个子集是有好处的(例如,如果有一个大的、难以散列的对象,它不可能是两个对象之间的唯一区别)

    对您来说,最原始、最正确的实现是:

    public int hashCode() {
        return 0;
    }
    

    这是正确的,但效率极低(因为每个对象都会散列到相同的值,很明显!)。

    我认为如果id是唯一的,那么应该只使用id字段来生成哈希代码。通过这种方式,您将能够通过仅使用id创建对象来从集合中搜索对象


    另外,如果您的id是唯一的,那么我们将为不同的对象使用不同的哈希代码,因此它将是有效的。

    哈希代码方法的实现取决于您希望如何进行bucketing-

  • 您可以选择将偶数id的所有对象放在一个桶中,将奇数id的对象放在另一个桶中
  • 或者你可以选择把年龄在0-10岁的人放在一个桶里,10-20岁的人放在另一个桶里,依此类推

  • 关键是,这实际上取决于对象在范围内的分布方式(如果对象被正确地划分为桶,您将获得更好的性能)以及您希望如何进行扣合。

    不要使用
    getClass()!=obj.getClass()
    。使用
    instanceof
    。否则,当您有子类时,它就会中断。如果不希望子类,则将类设置为final。但即便如此,也没有理由使用除
    instanceof
    之外的其他东西。没有什么比“理想的哈希代码实现”更合适的了。但是你的代码看起来足够了。您的
    hashCode
    实现必须与
    equals
    一致。既然两者都不使用
    age
    ,这是正确的。@霍尔格:在子类的情况下,它将如何中断?。很少考虑两个不同子类的实例相等。我想说你永远不应该这样做that@DevBlanked:如果您永远不想要子类,那么将类设置为final。否则,您必须考虑子类的
    等于
    的契约。查看
    equals
    实现的
    AbstractList
    作为示例。可能有很多不同的子类被认为是相同的。@Holger AbstractList场景有点特殊,因为它实现了一个接口。因此,检查instanceof非常有意义。但在这种情况下,比较类是正确的做法。如果您有HashA子类,HashB扩展Hash类。您永远不希望HashA的实例被视为等同于hashb的实例这里有n种自动生成hashcode的方法,但我想了解它背后的逻辑热情,即实现是错误的(即使使用
    HashCodeBuilder
    本身也不错):如果
    equals
    不关心
    age
    ,则
    hashCode
    不得使用它!我在最后一段加了一个问题来回答他的问题。该代码只是我从链接页面中选择的一个示例,它不是用来回答他的问题的。@Sage+1对于您现在的编辑,它是有意义的,但我不是downvoter@ankit对不起,我回复了约阿希姆。虽然我应该使用
    @
    符号。事实上,当你和他评论的时候,我正在编辑。我还想链接一些文章。这意味着我们只能使用.equals中使用的信息或其中的一个子集,但我们不应该使用任何额外的信息information@ankit:没错。您甚至可以使用空子集(即使这通常是一个坏主意)。好的,最后一个问题是这样做是强制性的,使用一些额外的信息有什么害处吗?这将使我们的哈希代码更强大only@ankit:如果“额外信息”的意思是“在equals中使用的额外信息”那么,我能想到的唯一可能的缺点是,您的哈希代码可能需要更长的时间来计算。这取决于您如何准确地使用可能相关或不相关的对象(很抱歉,答案含糊不清,但这确实取决于:如果您很少或曾经在
    HashSet
    HashMap
    中使用您的对象,那么整个
    hashCode
    辩论就没有多大关系了)。
        @Override
        public int hashCode() {
           return this.id % 2 == 0 ? 1 : 2;
        }
    
        @Override
        public int hashCode() {
           return this.age / 10 ;
        }