混淆Hashmap内部工作

时间:2015-12-04 11:26:39

标签: java hashmap

我无法理解以下代码中发生的事情。

我所做的是,我有一个外部课程Employee包含一些字段:id, panNo ,名称and地址. Then I have an inner class(it was not actually necessary to have it as an inner class) with id {{1 } panNo`字段及其值与其外部类成员相同。

根据我对and的了解,我们使用它们来存储键值对。 密钥具有HashMaps值,并且根据此hashcode值对值进行哈希处理。当我们在键的帮助下检索值时,再次评估其hashcode值,然后获取适当的值。

所以,hashcode必须是这样的:

键----->它的哈希码|参考字段-------->引用值对象。

因此,当我尝试插入具有相同键的对象时,插入最后一个的元素仅可用。这是因为,必须有唯一键,这意味着hashmap值应该只引用一个对象。

现在,我在代码中所做的是,我在地图中插入hashcode对象并使用Employee类作为返回相同EmployeeKey值的键,即1每次。所以根据我的理解,地图中应该只有一个元素。但这种情况并没有发生.....我不理解某些事情。

我写过以下课程:

hashcode

并且有一个测试类如下:

 package package1;
 public class Employee {
     int id;
     int panNo;
     String name;
     String address;

     public Employee(int id, int panNo, String name, String address) {
        this.id = id;
        this.panNo = panNo;
        this.name = name;
        this.address = address;
 }

 @Override
 public String toString() {
      return "Employee [id=" + id + ", panNo=" + panNo + ", name=" +   name + ", address=" + address + "]";
 }

 public class EmployeeKey {
    int id = Employee.this.id;
    int panNo = Employee.this.panNo;

    @Override
    public int hashCode() {
        return 1;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        EmployeeKey other = (EmployeeKey) obj;
        if (!getOuterType().equals(other.getOuterType()))
            return false;
        if (id != other.id)
            return false;
        if (panNo != other.panNo)
            return false;
        return true;
    }

    private Employee getOuterType() {
        return Employee.this;
    }

  }
}

2 个答案:

答案 0 :(得分:1)

以下是HashMap中密钥查找的各个阶段。

  1. 使用hash
  2. 获取密钥的key.hashCode()
  3. 使用该哈希查找应该放置此密钥的存储桶。
  4. 使用equals检查该存储桶中的每个密钥来搜索该存储桶中的密钥。
  5. 您已经hashCode返回1,所以您的所有商品都会放在同一个存储桶中,但equals仍然会为这两个键返回false。< / p>

        Employee e1 = new Employee(1, 123, "neeraj", "pune");
        Employee e2 = new Employee(2, 456, "viraaj", "pune");
    
        System.out.println(e1.new EmployeeKey().id);
    
        Map<Employee.EmployeeKey, Employee> myMap = new HashMap<Employee.EmployeeKey, Employee>();
        Employee.EmployeeKey k1 = e1.new EmployeeKey();
        myMap.put(k1, e1);
        Employee.EmployeeKey k2 = e2.new EmployeeKey();
        myMap.put(k2, e2);
    
        System.out.println("Size:" + myMap.size());
        System.out.println("Hashcode of inner class e1: "
                + e1.new EmployeeKey().hashCode());
        System.out.println("Hashcode of inner class e2: "
                + e2.new EmployeeKey().hashCode());
        System.out.println("Equals of keys: "
                + k1.equals(k2));
    
        System.out.println(myMap.get(e1.new EmployeeKey()));
    

答案 1 :(得分:1)

不,哈希表使用哈希码来决定哪个&#34;哈希桶&#34;用于钥匙。但哈希码不一定是唯一的。总是存在碰撞的可能性,这实际上经常发生。

因此,正确的哈希表仅使用哈希码作为决定元素存储位置的第一步,但它必须具有某种冲突解决方案 - 一种使用相同哈希码存储不同键的方法。这通常意味着在哈希表的每个元素中都有一个链表或一个实际键树。

当您将密钥放入哈希表时,它首先计算哈希码以进行第一个决策。但随后它使用equals()方法来确定它是否与该位置的现有密钥相同的密钥。如果是,则新条目将替换旧条目,并且密钥将保持唯一。但如果它不是相同的密钥 - equals()返回false - 那么尽管具有相同的哈希码,新密钥将被单独保存(添加到链表或树)。 / p>

当你想通过密钥检索一个值时,它会再次计算哈希码,但是然后使用equals()方法将密钥与该位置的所有密钥进行比较在表中(链表或树)。如果密钥等于存储的密钥,它将仅返回该值。如果没有相等的密钥,那么虽然哈希码是相同的,但密钥不被认为是相同的。

这就是为什么同时覆盖hashCode()equals()方法始终很重要,并确保它们是根据对象中的相同字段计算的。

为所有对象返回相同的哈希代码不会违反哈希表的唯一性,但它会降低其性能,因为有效地,而不是基于哈希表的哈希表计算hashCode()和有限数量的equals()调用时,哈希表中有一个有效单元格,并且您的数据结构已成为链接列表或树 - 使用线性搜索。