【问题标题】:Collision in hashmap哈希图中的冲突
【发布时间】:2017-08-30 18:35:04
【问题描述】:

我已阅读有关 Hashmap 冲突的信息并举了以下示例。由于我无法理解什么是碰撞以及如何避免发生碰撞。示例如下

import java.util.*;  
class JavaCollision{  
public static void main(String args[]){  

 HashMap<Person, String> map=new HashMap<Person, String>();
 Person p1 = new Person(1,"ABC");
 Person p2 = new Person(2,"DEF");
 Person p3 = new Person(1,"XYZ");
 Person p4 = new Person(1,"PQR");
 Person p5 = new Person(1,"PQR");
 System.out.println("Adding Entries ...."); 
 map.put(p1,"ONE");
 map.put(p2,"TWO");
 map.put(p3,"THREE");
 map.put(p4,"FOUR");
 map.put(p5,"FIVE");

 System.out.println("\nComplete Map entries \n" + map);

/* System.out.println("\nAccessing non-collided key");  
 System.out.println("Value = "+map.get(p2));
 System.out.println("\nAccessing collided key");    
 System.out.println("Value = " + map.get(p1));*/
 System.out.println("P1 hashcode is:"+map.get(p1).hashCode()+" And value is:"+map.get(p1));
 System.out.println("P2 hashcode is:"+map.get(p2).hashCode()+" And value is:"+map.get(p2));
 System.out.println("P3 hashcode is:"+map.get(p3).hashCode()+" And value is:"+map.get(p3));
 System.out.println("P4 hashcode is:"+map.get(p4).hashCode()+" And value is:"+map.get(p4));
 System.out.println("P5 hashcode is:"+map.get(p5).hashCode()+" And value is:"+map.get(p5));
}
}  

Person.java

public class Person {
private int id;
private String name;

public Person(int id, String name) { 
    this.id = id; this.name = name;
}

public String getName() { 
    return name;
}

public int getId() { 
    return id;
}

public void setId(int id) { 
    this.id = id;
}

public void setName (String name) { 
    this.name = name; 
}

public int hashCode(){
    System.out.println("Called hashcode for:"+id+" - "+name);
    return id;
}

public boolean equals(Object obj){
    System.out.println("Called equals on ="+id+" - "+name+" to compare with "+((Person)obj));
    boolean result=false;
    if(obj instanceof Person){
        if( ((Person)obj).getId() == id  && ((Person)obj).getName().equals(name) ){
            result=true;
            System.out.println("Result is true");
        }
    }
    return result;
}
public String toString() { 
    return id+" . "+name;
} 
}

结果是

Adding Entries ....
Called hashcode for:1 - ABC
Called hashcode for:2 - DEF
Called hashcode for:1 - XYZ
Called equals on =1 - XYZ to compare with 1 . ABC
Called hashcode for:1 - PQR
Called equals on =1 - PQR to compare with 1 . ABC
Called equals on =1 - PQR to compare with 1 . XYZ
Called hashcode for:1 - PQR
Called equals on =1 - PQR to compare with 1 . ABC
Called equals on =1 - PQR to compare with 1 . XYZ
Called equals on =1 - PQR to compare with 1 . PQR
Result is true

Complete Map entries 
{1 . ABC=ONE, 1 . XYZ=THREE, 1 . PQR=FIVE, 2 . DEF=TWO}
Called hashcode for:1 - ABC
Called hashcode for:1 - ABC
P1 hashcode is:78406 And value is:ONE
Called hashcode for:2 - DEF
Called hashcode for:2 - DEF
P2 hashcode is:83500 And value is:TWO
Called hashcode for:1 - XYZ
Called equals on =1 - XYZ to compare with 1 . ABC
Called hashcode for:1 - XYZ
Called equals on =1 - XYZ to compare with 1 . ABC
P3 hashcode is:79801726 And value is:THREE
Called hashcode for:1 - PQR
Called equals on =1 - PQR to compare with 1 . ABC
Called equals on =1 - PQR to compare with 1 . XYZ
Called hashcode for:1 - PQR
Called equals on =1 - PQR to compare with 1 . ABC
Called equals on =1 - PQR to compare with 1 . XYZ
P4 hashcode is:2158258 And value is:FIVE
Called hashcode for:1 - PQR
Called equals on =1 - PQR to compare with 1 . ABC
Called equals on =1 - PQR to compare with 1 . XYZ
Called equals on =1 - PQR to compare with 1 . PQR
Result is true
Called hashcode for:1 - PQR
Called equals on =1 - PQR to compare with 1 . ABC
Called equals on =1 - PQR to compare with 1 . XYZ
Called equals on =1 - PQR to compare with 1 . PQR
Result is true
P5 hashcode is:2158258 And value is:FIVE

在这个结果中 p4 和 p5 具有相同的哈希码。如何避免在 hashmap 中使用相同的 hashcode。我们可以通过返回哈希码来避免冲突吗?

【问题讨论】:

  • 如果你想避免冲突,请不要用不同的值散列相同的键。这就是你为p4 和p5 做的事情。
  • 使用一些返回唯一值的函数为您的对象生成哈希码。
  • 感谢您的回复。所以这里 p4 和 p5 对象是碰撞的吧?
  • @Muthuvigneshk 正确。基本上p5 在这里替换了p4,这就是为什么你看到P4 hashcode is:2158258 And value is:FIVE 而不是P4 hashcode is:2158258 And value is:FOUR
  • 您的代码没有意义:System.out.println("P5 hashcode is:"+map.get(p5).hashCode()+" And value is:"+map.get(p5)); 您没有显示 p5 的哈希码。您正在显示与 p5 关联的 value 的哈希码,这意味着您正在显示 "FIVE".hashCode() (与 HashMap 逻辑无关)。为了避免冲突,唯一的建议是构建一个更好的hashCode 函数。如果你的equals() 有name 参与相等性检查,你应该在哈希码计算中包括name。

标签: java collections hashmap collision


【解决方案1】:

你对HashMap 中的冲突有一个奇怪的理解。这实际上非常简单 - 当两个对象具有相同的哈希码之后内部重新散列在 HashMap 内完成:

static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

基本上在计算哈希码后,HashMap 将采用前 16 位,XOR 采用后 16 位 - 确保更好的分布。如果在此重新散列之后,两个 不同 键具有散列码值 - 这称为散列冲突。这意味着两个条目将最终在同一个存储桶中。

但这通常是性能瓶颈,因为放置在同一个桶中的条目(在达到一定数量后)被放入perfectly balanced red-black tree,搜索仍然非常快。例如,如果这个bucket中有2 pow 32条目,则需要ma​​x 32步才能找到这个条目;在这个Tree bucket 中,一般搜索时间是O(logn)。对于地图本身,搜索时间将是O(1) - 不变。

所以不要让自己复杂化 - 虽然你可以测试你的 Key#hashCode 以使其具有 更好 哈希码 - 不要那样做,除非你确定存在性能问题(我非常怀疑)。

【讨论】:

    【解决方案2】:

    在这个结果中 p4 和 p5 具有相同的哈希码。

    正确,但这不是这里的问题。问题是 p4 和 p5 具有相同的 key,即 id 的值。 Map 不包含重复项,因此 p4 被 p5 覆盖,因为它们具有相同的id,而不仅仅是相同的哈希码。

    如何避免在 hashmap 中使用相同的 hashcode。

    在 HashMaps 中没有必要避免冲突,这不是这里的问题。

    我们能否通过返回哈希码来避免冲突。

    是的,但这不是这里的问题。如果您希望在此映射中同时使用 p4 和 p5,则必须为它们提供不同的 键。 在这种情况下,这意味着您的 equals() 方法同时基于 id 和 @ 987654325@,你的hashCode()方法应该是:

    public int hashCode()
    {
        return id+name.hashCode(); // for example
    }
    

    【讨论】:

      猜你喜欢
      • 2012-05-13
      • 1970-01-01
      • 2016-07-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-10-21
      • 2017-09-28
      相关资源
      最近更新 更多