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?
答案 0 :(得分:82)
当您插入(10, 17)
对然后(10, 20)
时,技术上不会发生碰撞。您只是将旧值替换为给定键10
的新值(因为在这两种情况下,10等于10,而10的哈希码总是10)。
当多个密钥散列到同一个存储桶时发生冲突。在这种情况下,您需要确保可以区分这些键。链接冲突解决方案是用于此目的的技术之一。
举个例子,我们假设两个字符串"abra ka dabra"
和"wave my wand"
分别产生哈希码100
和200
。假设总数组大小为10,则它们最终都在同一个桶中(100 % 10
和200 % 10
)。链接可确保无论何时执行map.get( "abra ka dabra" );
,您都会得到与密钥关联的正确值。对于Java中的哈希映射,可以使用equals
方法完成此操作。
答案 1 :(得分:16)
在HashMap
中,密钥是一个对象,其中包含hashCode()
和equals(Object)
方法。
当您在Map中插入新条目时,它会检查hashCode
是否已知。然后,它将使用此哈希码迭代所有对象,并使用.equals()
测试它们的相等性。如果找到相等的对象,则新值将替换旧值。如果没有,它将在地图中创建一个新条目。
通常,在谈论地图时,如果两个对象具有相同的hashCode
但它们不同,则使用碰撞。它们在内部存储在列表中。
答案 2 :(得分:9)
确实可能形成一个链表。只是Map
合同要求它替换条目:
V put(K key, V value)
将指定的值与此映射中的指定键相关联 (可选操作)。如果地图以前包含映射 键,旧值由指定值替换。 (地图m是 据说当且仅当m.containsKey(k)包含密钥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)两次,但是 不同的值,multimap包含从键到两者的映射 值。
编辑:碰撞解决方案
这有点不同。当两个不同的密钥恰好具有相同的哈希码时发生冲突,或者具有不同哈希码的两个密钥碰巧映射到底层阵列中的同一个桶中。
考虑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
。您可以通过查看源代码自己查看:
答案 3 :(得分:2)
首先,你有一个哈希的概念有点错误,桑杰先生已经纠正了这个概念。
是的,Java确实实现了冲突解决技术。当两个键被散列到相同的值时(因为内部数组的大小是有限的,并且在某些时候hashcode()方法将返回两个不同键的相同散列值),此时会在存储桶中形成链接列表输入所有信息的位置,作为包含键值对的Map.Entry对象。如果在这样的列表中存在条目,则通过密钥访问对象将最坏地需要O(n)。您通过此类列表中的每个键传递的键之间的比较将通过equals()方法完成。
虽然从Java 8开始,链表被树替换(O(log n))
答案 4 :(得分:2)
碰撞和重复之间存在差异。 碰撞意味着hashcode和bucket是相同的,但是一式两份,它将是相同的hashcode,同一个桶,但这里等于方法进入图片。
检测到碰撞,您可以在现有密钥上添加元素。但如果重复,它将取代新的价值。
答案 5 :(得分:1)
您的示例中没有碰撞。您使用相同的密钥,因此旧值将替换为新值。现在,如果你使用两个映射到相同哈希码的键,那么你就会发生冲突。但即使在这种情况下,HashMap也会取代你的价值!如果您希望在发生碰撞时将值链接起来,则必须自己进行,例如:通过使用列表作为值。
答案 6 :(得分:1)
没有定义这样做。为了实现此功能,您需要创建一个将键映射到值列表的映射:
Map<Foo, List<Bar>> myMap;
或者,您可以使用the Multimap from google collections / guava libraries
答案 7 :(得分:1)
您的情况不是在讨论冲突解决,它只是用同一键的新值替换旧值,因为Java的HashMap
不能包含重复项(即,多个值 >)相同的键。
在您的示例中,对于HashMap中的相同键10,将值17替换为20。
如果您要为同一个键放置一个不同的/新值,则不是冲突解决方案的概念,而是简单地将旧值替换为相同键的新值。 HashMap
的设计方法就是这样,您可以查看以下来自here的API(重点是我的)。
公共V put(K键,V值)
将指定值与 此地图中指定的键。 如果地图之前包含地图 替换旧值。
另一方面,冲突解决技术仅在多个键以相同的哈希码结尾(即它们位于相同的存储桶位置)且已存储条目的情况下起作用。 HashMap
通过使用链接的概念来处理冲突解决方案,即,将值存储在链接列表(或Java8中的平衡树,取决于条目数)中。
答案 8 :(得分:1)
当多个键以相同的哈希码结尾时,该哈希码存在于同一存储桶中。 当同一键的值不同时,旧值将被新值替换。
在最坏的情况下,相似列表从病房的Java 8版本转换为平衡二叉树。
当两个不同的键生成相同的hashcode()值时发生冲突。 当冲突更多时,将导致哈希图的性能最差。
根据equals方法相等的对象必须返回相同的hashCode值。 当两个对象返回相同的代码时,它们将被移到相同的存储桶中。