Java HashMap中的冲突解决


68

JavaHashMap使用put方法在中插入K / V对HashMap。可以说我使用过putmethod,现在HashMap<Integer, Integer>有一个条目,key其值为10和value17。

如果我在其中插入10,20,HashMap由于键10相同而发生冲突,它会简单地用该条目替换之前的条目。

如果钥匙碰撞,则HashMap用新的K / V对替换旧的K / V对。

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

为什么它没有形成linkedlist键值为10且值为17,20的a?


嘿,谁拒绝所有这些正确答案?毕竟,Map接口需要该行为。
Axel

@Axel:我想那是因为人们误解了OP。OP基本上想知道将多个密钥散列到同一存储桶时会发生什么。即使用冲突解决方案时。其他所有答案都继续围绕着多张地图而不是其他内容做
Sanjay T. Sharma

2
但是OP明确给出了使用相同键(10)放置两个元素的示例,并且想知道为什么不同时存储两个不同的值。这就是MultiMap的行为。
Axel

可以使用HashMap.getEntry的源代码来确认。很明显,该条目是针对同一哈希码具有不同键值的列表。
Zaiping Bie 2015年

Answers:


102

当您插入线对(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方法完成的。


2
因此,存储桶将存储链的地址,并且链中将包含节点;每个节点都具有键/值结构?因此,在这种情况下,同一链中会有一个节点的键为“ abra ka dabra”,而另一个节点的键为“挥动我的手”,对吗?
user2938723

5
@ user2938723:是的,基本上每个数组插槽都将包含键值对的“链”。
Sanjay T. Sharma 2013年

那么Java使用哪种Collision处理机制?为什么?
dinesh kandpal

23

在一个HashMap关键是一个对象,它包含hashCode()equals(Object)方法。

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

通常,谈论地图时,当两个对象相同但不同时,就会使用碰撞hashCode。它们在内部存储在列表中。


9

实际上,它本可以形成一个链表。只是Map合同要求它替换条目:

V put(K key, V value)

将指定值与此映射中的指定键关联(可选操作)。如果该映射先前包含该键的映射,则旧值将替换为指定值。(仅当m.containsKey(k)返回true时,才认为映射m包含键k的映射。)

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

对于要存储值列表的地图,它必须为Multimap。这是Google的: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);
}

对于那些好奇Entry类中的HashMap行为如何像列表一样的人,事实证明它HashMap定义了自己的Entry实现的静态类Map.Entry。您可以通过查看源代码自己查看:

HashMap的GrepCode


“或者两个具有不同哈希码的键碰巧映射到基础数组中的同一存储桶中”。这将如何发生?我以为不同的哈希=不同的存储桶。是不是
艾哈迈德·阿卜杜勒加尼

3

首先,您已经有了散列有点错误的概念,并且Sanjay先生已将其纠正。

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

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


3

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

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

当两个不同的键生成相同的hashcode()值时发生冲突。当冲突更多时,将导致哈希图的性能最差。

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


2

冲突和重复之间有区别。冲突意味着哈希码和存储桶是相同的,但是在重复中,相同的哈希码和存储桶将是相同的,但是这里的方法等于图片。

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


2

你的情况是不是在谈论冲突解决,那简直是与相同的密钥的新值替换旧的价值,因为Java的HashMap不能包含重复(即多个值,对于相同的)关键

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

如果您要为同一个键输入一个不同的/新值,则不是冲突解决方案的概念,而是简单地将旧值替换为相同键的新值。它是HashMap这样设计的,您可以查看从此处获得的以下API(重点是我的)。

公开V投入(K键,V值)

将指定值与该映射中的指定键相关联。如果该映射先前包含该键的映射,则将替换旧值


另一方面,冲突解决技术仅在多个键以已存储条目的相同哈希码(即,它们位于同一存储桶位置)结尾时才起作用。HashMap通过使用链接的概念来处理冲突解决方案,即,将值存储在链接列表(或Java8中的平衡树,取决于条目数)中。



1

您的示例中没有冲突。您使用相同的键,因此旧值将替换为新值。现在,如果您使用了两个映射到相同哈希码的键,则将发生冲突。但是即使在这种情况下,HashMap也会取代您的价值!如果希望在发生冲突时将值链接起来,则必须自己完成操作,例如,使用列表作为值。


什么HashTable?如果您在说的是java.util.Hashtable,它实现了相同的Map接口并且具有相同的行为
iluxa 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.