我们来看一下 HashMap 的 put 操作:
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
当我们调用 put 存值时, HashMap 首先会调用 Key 的 hashcode() 方法,计算出哈希码,通过哈希码快速找到某个存放位置(桶),这个位置可以被称之为 bucketlndex(桶下标) ,但可能会存在多个元素找到了相同的 (桶下标) ,有个专业名词叫"哈希碰撞",当碰擅发生时,这时会取 bucketindex 位置已存储的元素,最终通过 equals 来比较 key 是否为同一对象,如果是,则使用新 Value 值替换旧 Value 值,并返回旧 Value 值,如果不是,则把新的键值对< K , V >存放到 bucketindex 位置。
对于哈希表数据结构来说: 如果s1对象和s2对象的hash值相同,一定是放到同一个单向链表上。
当然如果s1和s2的hash值不同,但由于哈希算法执行结束之后转换的 bucketlndex(桶下标)可能相同,此时会发生“哈希碰撞”。
一旦重写了equals()方法,就必须重写hashCode()方法。而且hashCode()的生成哈希值的依据应该是equals()中用来比较是否相等的字段。
如果两个由equals()规定相等的对象生成的hashCode不等,对于hashMap来说,他们很可能分别映射到不同位置,没有调用equals()比较是否相等的机会,两个实际上相等的对象可能被插入不同位置,出现错误。
哈希冲突如何解决呢?
哈希冲突的解决方案有4种:
1.开放地址法:(再散列法):当关键字key的哈希地址p出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。
2.再哈希法:在发生冲突的时候再用另外一个哈希函数算出哈希值,直到算出的哈希值不同为止。
3.链地址法(拉链法):例如HashMap。把同一个散列槽(数组的每一个槽)中的所有元素放到一个链表中。
对于关键字集合{12,67,56,16,25,37, 22,29,15,47,48,34},我们用前面同样的12为除数,进行除留余数法:

此时,已经不存在什么冲突换址的问题,无论有多少个冲突,都只是在当前位置给单链表增加结点的问题。