Java HashMap 中的冲突解决

Posted

技术标签:

【中文标题】Java HashMap 中的冲突解决【英文标题】:Collision resolution in Java HashMap 【发布时间】:2013-11-10 14:20:00 【问题描述】:

Java HashMap 使用put 方法在HashMap 中插入K/V 对。 假设我使用了put 方法,现在HashMap<Integer, Integer> 有一个条目,key 为 10,value 为 17。

如果我在这个HashMap 中插入 10,20,由于相同的键 10 发生冲突,它只是用这个条目替换上一个条目。

如果密钥冲突HashMap 用新的 K/V 对替换旧的 K/V 对。

所以我的问题是HashMap 何时使用链式冲突解决技术?

为什么它没有形成一个linkedlist,键为10,值为17,20?

【问题讨论】:

嘿,谁对所有这些正确答案投了反对票?毕竟 Map 接口需要这种行为。 @Axel:我想这是因为人们误解了 OP。 OP 基本上想知道当多个键被散列到同一个桶时会发生什么。那就是使用碰撞解决方案的时候。关于多地图的所有其他答案都在继续,什么不是...... 但是 OP 明确给出了将两个元素与相同的键 (10) 放置的示例,并想知道为什么不存储两个不同的值。这 MultiMap的行为。 这个可以通过HashMap.getEntry的源码来确认。很明显,该条目是一个具有相同哈希码的不同键值的列表。 【参考方案1】:

当您插入 (10, 17)(10, 20) 对时,从技术上讲不涉及冲突。您只是用给定键 10 的新值替换旧值(因为在这两种情况下,10 等于 10,而且 10 的哈希码始终为 10)。

当多个键散列到同一个存储桶时会发生冲突。在这种情况下,您需要确保可以区分这些键。链式碰撞解决方案是用于此目的的技术之一。

例如,假设两个字符串 "abra ka dabra""wave my wand" 分别产生哈希码 100200。假设总数组大小为 10,它们最终都在同一个存储桶中(100 % 10200 % 10)。链接确保无论何时执行map.get( "abra ka dabra" );,最终都会得到与键关联的正确值。对于 Java 中的哈希映射,这是通过使用 equals 方法完成的。

【讨论】:

所以bucket会存储链的地址,链会包含节点;每个节点都有一个键/值结构?因此,在这种情况下,链中将有一个节点的密钥为“abra ka dabra”,另一个节点的密钥为“挥动我的手”,对吗? @user2938723:是的,基本上每个数组槽都包含一个键值对“链”。 那么Java使用哪种碰撞处理机制?为什么?【参考方案2】:

HashMap 中,键是一个对象,其中包含hashCode()equals(Object) 方法。

当您在 Map 中插入新条目时,它会检查 hashCode 是否已知。然后,它将使用此哈希码遍历所有对象,并使用.equals() 测试它们的相等性。如果找到 equal 对象,则新值替换旧值。如果没有,它将在地图中创建一个新条目。

通常,谈到地图,当两个对象具有相同的hashCode 但它们不同时,您会使用collision。它们在内部存储在一个列表中。

【讨论】:

【参考方案3】:

确实,它可以形成一个链表。只是Map合约要求它替换入口:

V put(K key, V value)

将指定值与此映射中的指定键相关联 (可选操作)。如果该映射先前包含一个映射 键,旧值被指定值替换。 (地图 m 是 当且仅当 m.containsKey(k) 将返回 true。)

http://docs.oracle.com/javase/6/docs/api/java/util/Map.html

对于存储值列表的映射,它需要是 Multimap。这是谷歌的:http://google-collections.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Multimap.html

类似于 Map 的集合,但可能关联多个值 用一把钥匙。如果你调用 put(K, V) 两次,使用相同的键但是 不同的值,多​​重映射包含从键到两者的映射 价值观。

编辑:冲突解决

这有点不同。当两个不同的键碰巧具有相同的哈希码,或者具有不同哈希码的两个键碰巧映射到底层数组中的同一个桶时,就会发生冲突。

考虑HashMap的来源(已删除部分内容):

public V put(K key, V value) 
    int hash = hash(key.hashCode());
    int i = indexFor(hash, table.length);
    // i is the index where we want to insert the new element
    addEntry(hash, key, value, i);
    return null;


void addEntry(int hash, K key, V value, int bucketIndex) 
    // take the entry that's already in that bucket
    Entry<K,V> e = table[bucketIndex];
    // and create a new one that points to the old one = linked list
    table[bucketIndex] = new Entry<>(hash, key, value, e);

对于那些好奇HashMap 中的Entry 类如何表现得像一个列表的人,HashMap 定义了自己的静态Entry 类,它实现了Map.Entry。大家可以通过查看源代码自己了解:

GrepCode for HashMap

【讨论】:

"或者两个具有不同哈希码的键恰好映射到底层数组中的同一个桶中"。这将如何发生?我认为不同的哈希=不同的桶。不是吗?【参考方案4】:

首先,你对哈希的概念有点错误,它一直是rectified by @Sanjay。

是的,Java 确实实现了冲突解决技术。当两个键被哈希到相同的值时(因为使用的内部数组的大小是有限的,并且在某些时候 hashcode() 方法将为两个不同的键返回相同的哈希值),此时在桶中形成一个链表将所有信息作为包含键值对的 Map.Entry 对象输入的位置。如果条目存在于此类列表中,则通过键访问对象最多需要 O(n)。您传递的键与此类列表中的每个键之间的比较将由 equals() 方法完成。

尽管从 Java 8 开始,链表被替换为树 (O(log n))

【讨论】:

【参考方案5】:

您的案例不是在讨论冲突解决,它只是用相同键的新值替换旧值,因为 Java 的 HashMap 不能包含重复项(即多个)对于相同的

在您的示例中,对于 HashMap 中的相同键 10,值 17 将简单地替换为 20。

如果您尝试为同一个键设置不同的/新值,这不是冲突解决的概念,而是简单地将旧值替换为同一个键的新值。这就是 HashMap 的设计方式,您可以查看以下从 here 获取的 API(重点是我的)。

public V put(K key, V value)

将指定的值与 此映射中的指定键。 如果地图之前包含映射 对于键,旧值被替换


另一方面,冲突解决技术只有在多个键最终具有相同的哈希码(即它们落在同一个存储桶位置)时才会发挥作用,其中已经存储了一个条目。 HashMap 通过使用链的概念来处理冲突解决,即,它将值存储在链表中(或从 Java8 开始的平衡树,取决于条目的数量)。

【讨论】:

【参考方案6】:

当多个键以相同的哈希码结束时,该哈希码存在于同一个桶中。 当同一个键有不同的值时,旧值将被替换为新值。

在最坏的情况下,喜欢的列表从 java 8 版本转换为平衡二叉树。

当 2 个不同的键生成相同的 hashcode() 值时会发生冲突。 当有更多的冲突时,就会导致 hashmap 的性能最差。

根据equals方法相等的对象必须返回相同的hashCode值。 当两个对象返回相同的代码时,它们将被移动到同一个桶中。

【讨论】:

【参考方案7】:

冲突和重复是有区别的。 碰撞意味着哈希码和桶是相同的,但是在重复时,它将是相同的哈希码,相同的桶,但是这里的equals方法出现在图片中。

检测到碰撞,您可以在现有键上添加元素。但如果出现重复,它将替换新值。

【讨论】:

【参考方案8】:

它没有被定义为这样做。为了实现此功能,您需要创建一个映射,将键映射到值列表:

Map<Foo, List<Bar>> myMap;

或者,您可以使用the Multimap from google collections / guava libraries

【讨论】:

【参考方案9】:

您的示例中没有冲突。您使用相同的密钥,因此旧值将被新值替换。现在,如果您使用映射到相同哈希码的两个键,那么您就会发生冲突。但即使在那种情况下,HashMap 也会取代你的价值!如果您希望在发生冲突时将值链接起来,您必须自己做,例如通过使用列表作为值。

【讨论】:

什么是HashTable?如果您说的是java.util.Hashtable,它实现了相同的Map 接口并且具有相同的行为

以上是关于Java HashMap 中的冲突解决的主要内容,如果未能解决你的问题,请参考以下文章

Java面试小短文HashMap是如何解决Hash冲突的?

Java面试小短文HashMap是如何解决Hash冲突的?

Java面试小短文HashMap是如何解决Hash冲突的?

Java面试小短文HashMap是如何解决Hash冲突的?

HashMap中的散列函数冲突解决机制和rehash

HashMap解决hash冲突的方法