先说Java之外的,什么是拉链法?怎么解决冲突的:

拉链法解决冲突的做法是:将所有关键字为同义词的结点链接在同一个单链表中。

若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组t[0..m-1]。凡是散列地址为i的结点,均插入到以t为头指针的单链表中。

t中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于1,但一般均取α≤1。

换句话说:HashCode是使用Key通过Hash函数计算出来的,由于不同的Key,通过此Hash函数可能会算的同样的HashCode,

所以此时用了拉链法解决冲突,把HashCode相同的Value连成链表. 但是get的时候根据Key又去桶里找,如果是链表说明是冲突的,此时还需要检测Key是否相同

在解释下,Java中HashMap是利用“拉链法”处理HashCode的碰撞问题。

在调用HashMap的put方法或get方法时,都会首先调用hashcode方法,去查找相关的key,当有冲突时,再调用equals方法。

hashMap基于hasing原理,我们通过put和get方法存取对象。

当我们将键值对传递给put方法时,他调用键对象的hashCode()方法来计算hashCode,然后找到bucket(哈希桶)位置来存储对象。

当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当碰撞发生了,对象将会存储在链表的下一个节点中。

hashMap在每个链表节点存储键值对对象。当两个不同的键却有相同的hashCode时,他们会存储在同一个bucket位置的链表中。

键对象的equals()来找到键值对。HashMap的put和get方法源码如下:

/**
* Returns the value to which the specified key is mapped,
* or if this map contains no mapping for the key.
*
* 获取key对应的value
*/
public V get(Object key) {
if (key == null)
return getForNullKey();
//获取key的hash值
int hash = hash(key.hashCode());
// 在“该hash值对应的链表”上查找“键值等于key”的元素
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;
} /**
* Offloaded version of get() to look up null keys. Null keys map
* to index 0.
* 获取key为null的键值对,HashMap将此键值对存储到table[0]的位置
*/
private V getForNullKey() {
for (Entry<K,V> e = table[0]; e != null; e = e.next) {
if (e.key == null)
return e.value;
}
return null;
} /**
* Returns <tt>true</tt> if this map contains a mapping for the
* specified key.
*
* HashMap是否包含key
*/
public boolean containsKey(Object key) {
return getEntry(key) != null;
} /**
* Returns the entry associated with the specified key in the
* HashMap.
* 返回键为key的键值对
*/
final Entry<K,V> getEntry(Object key) {
//先获取哈希值。如果key为null,hash = 0;这是因为key为null的键值对存储在table[0]的位置。
int hash = (key == null) ? 0 : hash(key.hashCode());
//在该哈希值对应的链表上查找键值与key相等的元素。
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 != null && key.equals(k))))
return e;
}
return null;
} /**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*
* 将“key-value”添加到HashMap中,如果hashMap中包含了key,那么原来的值将会被新值取代
*/
public V put(K key, V value) {
//如果key是null,那么调用putForNullKey(),将该键值对添加到table[0]中
if (key == null)
return putForNullKey(value);
//如果key不为null,则计算key的哈希值,然后将其添加到哈希值对应的链表中
int hash = hash(key.hashCode());
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//如果这个key对应的键值对已经存在,就用新的value代替老的value。
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
} modCount++;
addEntry(hash, key, value, i);
return null;
}

从HashMap的put()和get方法实现中可以与拉链法解决hashCode冲突解决方法相互印证。

并且从put方法中可以看出HashMap是使用Entry<K,V>来存储数据。数据节点Entry的数据结构如下:

 // Entry是单向链表。
// 它是 “HashMap链式存储法”对应的链表。
// 它实现了Map.Entry 接口,即实现getKey(), getValue(), setValue(V value), equals(Object o), hashCode()这些函数
static class Entry<K,V> implements Map.Entry<K,V> {
final K key;
V value;
//指向下一个节点
Entry<K,V> next;
final int hash; /**
* Creates new entry.
* 输入参数包括"哈希值(h)", "键(k)", "值(v)", "下一节点(n)"
*/
Entry(int h, K k, V v, Entry<K,V> n) {
value = v;
next = n;
key = k;
hash = h;
} public final K getKey() {
return key;
} public final V getValue() {
return value;
} public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
} // 判断两个Entry是否相等
// 若两个Entry的“key”和“value”都相等,则返回true。
// 否则,返回false
public final boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry e = (Map.Entry)o;
Object k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
Object v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
} public final int hashCode() {
return (key==null ? 0 : key.hashCode()) ^
(value==null ? 0 : value.hashCode());
} public final String toString() {
return getKey() + "=" + getValue();
} /**
* This method is invoked whenever the value in an entry is
* overwritten by an invocation of put(k,v) for a key k that's already
* in the HashMap.
*/
void recordAccess(HashMap<K,V> m) {
} /**
* This method is invoked whenever the entry is
* removed from the table.
*/
void recordRemoval(HashMap<K,V> m) {
}
}

从这段代码中,我们可以看出Entry是一个单向链表,这也是我们为什么说HashMap是通过拉链法解决hash冲突的原因。Entry实现了Map.Entry接口。

HashMap对HashCode碰撞的处理的更多相关文章

  1. HashMap之Hash碰撞源码解析

    转自:https://blog.csdn.net/luo_da/article/details/77507315 https://www.cnblogs.com/tongxuping/p/827619 ...

  2. hashMap的hashCode() 和equal()的使用

    hashMap的hashCode() 和equa()的使用 在java的集合中,判断两个对象是否相等的规则是: ,判断两个对象的hashCode是否相等 如果不相等,认为两个对象也不相等,完毕 如果相 ...

  3. HashMap中hashCode()和equals()重要性

    Java中HashMap根据hashCode()和equals()方法来获取键值对的索引,同时也通过这两个方法由key值获取value值.如果没有这两个方法,那么当有两个相同的 hash值时,可能会被 ...

  4. HashMap与HashCode有关,用Sort对象排序

    遍历Map,使用keySet()可以返回set值,用keySet()得到key值,使用迭代器遍历,然后使用put()得到value值. 上面这个算法的关键语句: Set s=m.keySet(); I ...

  5. HashMap通过hashcode对其内容进行快速查找,而 TreeMap中所有的元素都保持着某种固定的顺序

    HashMap通过hashcode对其内容进行快速查找,而 TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不 ...

  6. HashMap之Hash碰撞冲突解决方案及未来改进

    说明:参考网上的两篇文章做了简单的总结,以备后查(http://blogread.cn/it/article/7191?f=wb  ,http://it.deepinmind.com/%E6%80%A ...

  7. HashMap怎样解决碰撞问题

    碰撞:HashMap运用put方法存储多个元素时,计算得出相同的hashCode,在put时出现冲突. 处理:利用“拉链法”处理HashCode的碰撞问题:当我们将键值对传递给put方法时,他调用键对 ...

  8. 对hashmap与hashcode()、equals()的理解

    1.equals方法没被重写的时候   比较的只是对象的地址  重写之后 比较的才是对象里的内容 2.重写equals的时候 务必需要重写hashcode 不然在用到容器的时候 会出现问题 因为容器会 ...

  9. HashMap/HashSet,hashCode,哈希表

    hash code.equals和“==”三者的关系 1) 对象相等则hashCode一定相等: 2) hashCode相等对象未必相等. == 是比较地址是否相等,JAVA中声明变量都是引用嘛,不同 ...

随机推荐

  1. 【3dsMax安装失败,如何卸载、安装3dMax 2014?】

    AUTODESK系列软件着实令人头疼,安装失败之后不能完全卸载!!!(比如maya,cad,3dsmax等).有时手动删除注册表重装之后还是会出现各种问题,每个版本的C++Runtime和.NET f ...

  2. hibernate离线查询DetachedCriteria清除上次的查询条件

    1 原例概述 别名重复问题之后,我们还需要解决的问题就是: 如何清除hibernate的上次查询条件,如果不清除,将会导致上次的查询条件和下次的查询条件合并到了一起. 上次的查询条件和本次的查询条件合 ...

  3. TOJ 2815 Connect them (kruskal+并查集)

    描述 You have n computers numbered from 1 to n and you want to connect them to make a small local area ...

  4. javascript window.open in safari

    在ios系统中,无法使用 window.open 打开url,经过一番尝试终于找到了解决办法 var url='http://www.baodu.com'; var deviceAgent = nav ...

  5. tomcat server 报错之 More than the maximum allowed number of cookies

    More than the maximum allowed number of cookies EVERE: Error processing request java.lang.IllegalArg ...

  6. Please, configure Web Facet first!idea报这错的解决办法!!

    Please, configure Web Facet first!idea报这错的解决办法!! 今天在idea导入用eclipse的项目,然后运行项目的时候报这个错, 看下图 网上找了好多都没解决, ...

  7. 常见的media断点

    landscape mode是指宽度比高度宽的模式,也就是俗称的宽屏模式: portrait mode是指高度比宽度高的模式,也就是俗称的竖屏模式: /*#region SmartPhones */ ...

  8. js实现手风琴效果

    之前在慕课网上有练习手风琴效果,但是老师使用jquery简简单单的两三行实现了,今天自己用js练习一下效果 <div id="divbox"> <ul> & ...

  9. Linux服务器性能评估与优化(二)

    网络内容总结(感谢原创) 1.Linux内核参数优化 内核参数是用户和系统内核之间交互的一个接口,通过这个接口,用户可以在系统运行的同时动态更新内核配置,而这些内核参数是通过Linux Proc文件系 ...

  10. sheepdog