【问题标题】:how will be the hashcode calculated for objects created for collection data types in java?如何为 java 中为集合数据类型创建的对象计算哈希码?
【发布时间】:2016-11-08 16:19:10
【问题描述】:

根据最近的研究,我的理解是 hashCode 是一种方法,它为针对特定数据类型实例化的对象返回唯一整数。但是为所有数据类型对象计算哈希码将使用相同的算法还是会根据某些逻辑而变化?我没有得到太多关于collection datatypes的hashcode计算机制的信息。如果有任何澄清或参考将不胜感激。

【问题讨论】:

    标签: java collections hashcode


    【解决方案1】:

    计算hashCode 取决于类。没有标准规则。

    你只需要遵守here解释的规则:

    hashCode的总合约是:

    • 只要在 Java 应用程序执行期间对同一个对象多次调用,hashCode 方法必须始终返回相同的整数,前提是没有修改对象上的 equals 比较中使用的信息。该整数不需要在应用程序的一次执行与同一应用程序的另一次执行之间保持一致。
    • 如果两个对象根据 equals(Object) 方法相等,则对两个对象中的每一个调用 hashCode 方法必须产生相同的整数结果。
    • 如果根据 equals(java.lang.Object) 方法,如果两个对象不相等,则不要求对两个对象中的每一个调用 hashCode 方法必须产生不同的整数结果。但是,程序员应该意识到,为不相等的对象生成不同的整数结果可能会提高哈希表的性能。

    这部分答案是为了解释对Qwerky的答案的一个评论,抱歉但不容易在cmets上展示完整的代码片段。

    这里有一个简单的代码,解释了为什么List 不能用作地图中的键

        Map<List, String> map = new HashMap<List, String>();
        List list = new ArrayList();
        map.put(list, "a");
        System.out.println("Before: " + map.get(list)); // Print A
        list.add(new Object());
        System.out.println("After: " + map.get(list)); // Print null
    

    请注意,列表遵循hashCode的合同。

    但是正如 Brian Goetz 和 Josh Bloch 所指出的:

    如果一个对象的hashCode()值可以根据它的状态而改变,那么我们在使用这些对象作为基于哈希的集合中的键时必须小心,以确保我们不允许它们在使用时改变它们的状态作为哈希键。 所有基于散列的集合都假定对象的散列值在用作集合中的键时不会改变如果键的哈希码在集合中发生变化可能会出现一些不可预测和令人困惑的后果。这在实践中通常不是问题 - 在 HashMap 中使用像 List 这样的可变对象作为键并不常见

    【讨论】:

    • 请发表评论以显示答案中的错误。
    • 不是downvoter,但列表可以用作键,与任何其他对象相同。对于hashCode()equals() (对于HashMapcompareTo() (对于TreeMap(),密钥必须是不可变的要求适用于所有种键,而不仅仅是列表。 任何用作键的对象对象是键时不得更新。 --- 另外,问题根本不是关于地图的,那为什么还要谈论地图键的限制呢?
    • hashCode 不是标准函数。 hashCode 的用途与 HashMap 严格相关。所有对象都必须遵循一个明确的契约,而列表则不遵循它。已明确引用列表以完成对 Qwerky 答案的评论
    • @Andreas 我添加了对 Qwerty 答案的明确引用以更清楚。感谢您的评论
    • List 没有遵循合同的哪一部分?它遵循所有 3 条规则,没有失败。正如已经向您评论的那样,第一条规则说 “hashCode 方法必须始终返回相同的整数,提供了没有修改对象上的 equals 比较中使用的信息List 完全符合该规则,即哈希码保持不变,只要列表或其任何元素未更改。您的代码示例丢失数据是因为 you 违反了规则,而不是 List更改了列表。这不是 List 的错,它按照规则做的一切都是正确的。
    【解决方案2】:

    您好,默认的 hashcode 方法返回对象引用为 classname@Soome 唯一的数值,但是在收集的情况下,我们重写 hashcode 方法来生成我们的自定义键,同时我们重写 equals 方法来获取正确的输出。

    你可以从我下面的代码中得到详细的想法

    package com.rk.collections;
    
    import java.util.HashMap;
    import java.util.Map;
    
    class Employee
    {
        private String name;
        private int regdno;
        private String city;
       private  String college;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getRegdno() {
        return regdno;
    }
    public void setRegdno(int regdno) {
        this.regdno = regdno;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getCollege() {
        return college;
    }
    public void setCollege(String college) {
        this.college = college;
    }
    @Override
    public int hashCode() {
    
    
        return regdno;
    }
    
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Employee other = (Employee) obj;
    
    
        if (regdno != other.regdno)
    
    
        return false;
        return true;
    }
    
    
    public String toString() {
        return "Employee [name=" + name + ", regdno=" + regdno + ", city=" + city + ", college=" + college + "]";
    }
    
    
    
    }
    
    public class MapDemo {
     public static void main(String []args)
     {
         Employee e1=new Employee();
         e1.setName("Amlan");
         e1.setRegdno(326);
         e1.setCity("Bhanjanagar");
         e1.setCollege("IGIT");
    
         Employee e2=new Employee();
         e2.setName("Amlan");
         e2.setRegdno(325);
         e2.setCity("Sambalpur");
         e2.setCollege("IGIT");
    
         Employee e3=new Employee();
         e3.setName("Tapas");
         e3.setRegdno(324);
         e3.setCity("Baripada");
         e3.setCollege("IGIT");
    
         HashMap<Employee,Employee> hs=new HashMap();
         hs.put(e1,e1);
         hs.put(e2,e2);
         hs.put(e3,e3);
         System.out.println(hs.size());
         for(Map.Entry m:hs.entrySet())
         {
             System.out.println(m.getValue());
         }
    
     }
    
    
    
    }
    

    【讨论】:

      【解决方案3】:

      对于它的价值,这是AbstractList 如何计算它的 hashCode;

      /**
       * Returns the hash code value for this list.
       *
       * <p>This implementation uses exactly the code that is used to define the
       * list hash function in the documentation for the {@link List#hashCode}
       * method.
       *
       * @return the hash code value for this list
       */
      public int hashCode() {
          int hashCode = 1;
          for (E e : this)
              hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
          return hashCode;
      }
      

      此外,哈希码不一定是唯一的。

      【讨论】:

      • 不用看代码——在the javadoc
      • @Qwerky 这是最糟糕的 hashCode 示例。 hashCode 用于将元素作为键放入 Map 中。随时间变化的列表不遵循一般约定,特别是:“在 Java 应用程序执行期间,只要在同一个对象上多次调用它,hashCode 方法必须始终返回相同的整数”
      • @DavideLorenzoMARINO - 不要怪我,它的核心 java 代码。你也错过了你的报价,可能是重要的一点provided no information used in equals comparisons on the object is modified。如果两个列表相等并且您更改其中一个列表,则它们不再相等,因此它们应该具有不同的哈希码。
      • @Qwerky 问题不在于我们没有谈论 equals (你说得对,equals 必须写得好),而是 List 不能用作 Map 中的键,因为它更改其内容时更改其 hashCode。所以不是一个很好的例子。可能它的用途是用于不可修改的列表,
      • @Querky 如果您使用地图中的列表作为键。你改变了列表的内容。您无法使用该键检索原始对象。任何在其生命中改变其 hashCode 值的对象都不能在 map 中用作键!
      猜你喜欢
      • 2014-07-29
      • 1970-01-01
      • 1970-01-01
      • 2010-09-11
      • 2021-11-07
      • 1970-01-01
      • 1970-01-01
      • 2019-05-03
      • 1970-01-01
      相关资源
      最近更新 更多