如果Key的哈希码相同但是equals方法返回false,HashMap如何检索不同的值

时间:2015-01-08 09:22:28

标签: java collections hashmap

我无法理解HashMap的工作模式。请帮助理解它。

假设我们有两个对象 Obj1 Obj2 具有与 1212 相同的Hashcode。现在当我们运行" =="并且等于它返回false。

现在我使用 ValueObj1 Valueobj2 作为HashMap中的值,分别为 Obj1 Obj2 我相信这两个值都将保存在与List 相同的存储桶中。

我的问题是HashMap如何为Obj2选择 Valueobj2,为Obj1选择ValueObj1 。假设有n ..这样的对象和值。 这个键怎么样 - >值关联在内部工作,即使哈希码相同但值不同。

假设两个条件等于未被覆盖和覆盖

5 个答案:

答案 0 :(得分:12)

HashMap / HashSet每个桶实现一个键列表(在Map的情况下与值​​一起)。如果多个密钥共享相同的hashCode值,则会将它们放在此列表中。

因此,搜索方法首先提取查询键的hashCode,然后迭代相应的列表,直到equals方法成功。如果是HashSet,则表示找到key,如果是HashMap,则返回元组的另一侧:值。

HashMap的记忆就像:

+--------+--------+--------+--------+
|   00   |   01   |   10   |   11   |
+--------+--------+--------+--------+
    |        |        |        |
 k00/v00     _     k06/v06     _
    |                 |
 k08/v08           k14/v14
    |                 |
 k04/v04              _
    |
    _

你看到的是四个水桶的顶部。每个存储桶都有一个列表(下面的项目),用于存储密钥元组(k)和值(v)。由于此处只有四个存储桶,因此哈希算法使用模4运算,因此值k06的密钥v06将放置在存储区06 mod 4 = 02中,从而10。如果第二个密钥k1414 mod 4 = 02 10一起添加,则会将其添加到列表中。

由于值也与它一起存储,因此可以执行快速查找操作。因此,键与值一起存储。

你注意到,迭代(链接)列表是一项昂贵的操作。但是HashMap的意思是,人们希望使用正确术语(共享同一个桶的密钥数)的哈希冲突的数量非常少。通常,人们可能期望每个桶有两个或三个元素。因此,通过在恒定时间内选择正确的桶来实现性能提升,搜索存储桶需要线性时间(或者如果密钥有完整的排序,可以实现(平衡)二进制)树以对数时间搜索)。但最糟糕的是,HashMap可以实现与ArrayList / LinkedList条目相同的性能,但鉴于哈希 -function已经设计得很好,赔率非常低。

答案 1 :(得分:11)

You can always look at the code.

 public V get(Object key) {
     if (key == null)
         return getForNullKey();
     int hash = hash(key.hashCode());
     for (Entry<K,V> e = table[indexFor(hash, table.length)];
          e != null;
          e = e.next) {
         Object k;
         if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
             return e.value;
     }
     return null;
 }
  1. 因此它首先获取给定密钥的哈希值。
  2. 使用该哈希定位表(在其他答案中称为存储桶)。
  3. 对于存储桶中的每个条目,它会测试表{0}}键是否为键,如果是,则表示找到了正确的项。
  4. 通过哈希对存储桶进行分区会减少使用equals比较进行线性搜索的大小。因此,您可以看到为哈希码返回固定值有多么有害。有关良好哈希码计算的提示,请参阅this

答案 2 :(得分:3)

HashMap的工作原理是根据密钥的哈希值将其内容划分为桶。每个存储桶依次包含一个条目列表,一个由密钥和值组成的条目。

我们想说我们想在地图中查找x。我们计算x.hashCode()并选择适当的桶。然后,我们遍历存储桶列表并选择e等于e.key的条目x。然后我们返回e.value

伪代码:

class Map {
    class Entry {
        Object key, value;
    }

    List<List<Entry>> buckets;

    Object get(Object key) {
        List<Entry> bucket = buckets.get(key.hashCode() % buckets.size());
        for (Entry entry : bucket) {
            if (Object.equals(key, entry.key) return entry.value;
        }
        return null;
    }
}

(免责声明:使用%来计算一个桶指数过于简单化,并不会按原样运作;它只是传达了一般的想法)

答案 3 :(得分:0)

调用

hashcode()方法并计算哈希码。此hashcode用于查找用于存储Entry对象的数组索引。

indexFor(hash,table.length)用于计算表数组中用于存储Entry对象的精确索引。

具有相同hashcode的两个关键对象(称为碰撞)

在哈希映射中,bucket使用简单的链表来存储对象。

如果两个密钥具有相同的哈希码,则将键值对存储在与现有密钥相同的桶中。

当具有相同哈希码的两个键存储在hashmap中时,如何检索值对象? 使用hashcode wo转到右侧桶并使用equals我们在桶中找到正确的元素然后返回它。

HashMap get()函数

如果key不为null,则它将在key对象上调用hashfunction。

int hash = hash(hashValue)

hashvalue用于查找存储Entry对象的存储区位置。条目对象存储在存储桶中,如(hash,key,value,bucketindex)

详细信息已阅读herehere

答案 4 :(得分:0)

将两个对象与==进行比较并不是一个好主意,因为它会检查两个对象是否实际上是指向内存中相同对象的链接。

Wikipedia上有关于哈希表的好article。 java中的Hashmap内部有一个“桶”数组。

当您添加新对<key, value>(或在您的情况下为<obj1, valueObj1>)时,根据obj1.hashcode()计算存储桶编号。此对添加到选定的存储桶中,该存储桶内部为LinkedList,用于存储实际的对<key, value>

当您尝试使用密钥对象valueObj1搜索obj1时,哈希映射计算该对所在的桶号,并迭代所有LinkedList个元素比较密钥equals()。如果equals()立即返回true,则表示找到了我们要查找的元素。