最近看HashMap的源码,其中相同下标容易产生hash冲突,但是调试需要发生hash冲突,本文模拟hash冲突。

hash冲突原理

HashMap冲突是key首先调用hash()方法:

static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

然后使用hash值和tab数组长度做与操作:

 (n - 1) & hash

算出来的下标,如果一致就会产生冲突。

通过ASKII码获取单个字符

开始想到单字符,比如a、b、c、d、e这类字符,但是如果一个一个试的话特别繁琐,想到了ASKII码:

遍历1~100ASKII码。通过ASKII码获取单字符:

for (int i = 33; i < 100; i++) {
char ch = (char) i;
String str = String.valueOf(ch);
}

通过str获取下标,HashMap默认长度为16,所以n-1为15:

int index = 15 & hash(str);

获取发生hash冲突的字符

算出index一致的话,就放在一个列表中。不同的index放在HashMap中,完整代码如下:

Map<Integer, List<String>> param = new HashMap<>();
for (int i = 33; i < 100; i++) {
char ch = (char) i;
String str = String.valueOf(ch);
int index = 15 & hash(str);
List<String> list = param.get(index);
if (list == null) {
list = new ArrayList<>();
}
list.add(str);
param.put(index,list);
}
param.forEach((k,v) -> System.out.println(k + " " + Arrays.toString(v.toArray())));

输出结果:

0 [0, @, P, `]
1 [!, 1, A, Q, a]
2 [", 2, B, R, b]
3 [#, 3, C, S, c]
4 [$, 4, D, T]
5 [%, 5, E, U]
6 [&, 6, F, V]
7 [', 7, G, W]
8 [(, 8, H, X]
9 [), 9, I, Y]

源码调试

根据上面算出来的结果,使用其中的一个例子:

1 [!, 1, A, Q, a]

先添加数据:

 Map<String,Integer> map = new HashMap<>();
map.put("!",1);
map.put("1",1);
map.put("A",1);

先添加1, A, Q三个数据。然后添加Q

打开调式,定位到putVal方法:

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}

在源码解析文章详解HashMap源码解析(下)中知道,发生hash冲突是会在上面代码的第16行,一直for循环遍历链表,替换相同的key或者在链表中添加数据:

for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}

调式:

会一直遍历for循环,直到p.next==null遍历到链尾,然后在链表尾部添加节点数据:

p.next = newNode(hash, key, value, null);

总结

  • 通过(h = key.hashCode()) ^ (h >>> 16)高位运算hash码(n - 1) & hash哈希表数组长度取模,分析hash冲突原理。
  • 通过ASKII码遍历获取字符串,获取发生hash冲突的字符。
  • 调用put方法,调用hash冲突源码。

模拟HashMap冲突的更多相关文章

  1. hashmap冲突的解决方法以及原理分析:

    在Java编程语言中,最基本的结构就是两种,一种是数组,一种是模拟指针(引用),所有的数据结构都可以用这两个基本结构构造,HashMap也一样.当程序试图将多个 key-value 放入 HashMa ...

  2. 扰动函数和拉链法模拟HashMap的存储结构

    HashMap是Map接口下面的子孙,它对外是K,V结构存储的,而内部也着自己的存储结构,它的get操作是O(1)的时间复杂度,可以说是非常快的找到目录,而添加时,也是O(1),所以在键值存储里,它成 ...

  3. JavaScript 模拟 HashMap例子

    function map(){    var map = {}; // Map map = new HashMap();        var key = "key";    va ...

  4. 【数据结构】27、红黑树,节点插入,修复平衡操作总结(针对jdk8中hashmap冲突过多链表转红黑树)

    二叉树节点插入 0.如果只有一个节点,那么就直接作为根,涂黑,如果父为黑,或者祖父为空,那么不做操作 1.叔叔节点不为空且为红 那么就修改父,叔叔,祖父节点颜色,最后把当前节点设置为祖父节点,在进行平 ...

  5. 大厂面试必问!HashMap 怎样解决hash冲突?

    HashMap冲突解决方法比较考验一个开发者解决问题的能力. 下文给出HashMap冲突的解决方法以及原理分析,无论是在面试问答或者实际使用中,应该都会有所帮助. 在Java编程语言中,最基本的结构就 ...

  6. 多线程情况下HashMap死循环的问题

    1.多线程put操作后,get操作导致死循环. 2.多线程put非null元素后,get操作得到null值. 3.多线程put操作,导致元素丢失. 死循环场景重现 下面我用一段简单的DEMO模拟Has ...

  7. 深入理解JAVA集合系列三:HashMap的死循环解读

    由于在公司项目中偶尔会遇到HashMap死循环造成CPU100%,重启后问题消失,隔一段时间又会反复出现.今天在这里来仔细剖析下多线程情况下HashMap所带来的问题: 1.多线程put操作后,get ...

  8. 深入理解java集合框架之---------HashMap集合

    深入理解HaspMap死循环问题 由于在公司项目中偶尔会遇到HashMap死循环造成CPU100%,重启后问题消失,隔一段时间又会反复出现.今天在这里来仔细剖析下多线程情况下HashMap所带来的问题 ...

  9. 手撸HashMap实现

    前言 HashMap是Java中常用的集合,而且HashMap的一些思想,对于我们平时解决业务上的一些问题,在思路上有帮助,基于此,本篇博客将分析HashMap底层设计思想,并手写一个迷你版的Hash ...

随机推荐

  1. 论文解读(GROC)《Towards Robust Graph Contrastive Learning》

    论文信息 论文标题:Towards Robust Graph Contrastive Learning论文作者:Nikola Jovanović, Zhao Meng, Lukas Faber, Ro ...

  2. docker容器的持久化存储:Volume

    独立于docker容器的持久化存储: 法(1):自动将服务器文件夹挂载到容器内部文件夹/usr/share/nginx/html,这样只修改服务器文件夹下的内容即可对应修改容器内部文件夹的内容 将服务 ...

  3. XCTF练习题---WEB---Cookie

    XCTF练习题---WEB---Cookie flag:cyberpeace{dc6a6799546a3e0fbfeacb8650b55ff0} 解题步骤: 1.观察题目,打开场景 2.观察场景内容, ...

  4. Java 16 新特性:instanceof增强

    instanceof这个关键词,主要用来判断某个对象是不是某个类的实例. 比如,有时候我们要处理一个类似这样的数据集: Map<String, Object> data = new Has ...

  5. spring 拦截器流程 HandlerInterceptor AsyncHandlerInterceptor HandlerInterceptorAdapter

    HandlerInterceptor源码 3种方法: preHandle:拦截于请求刚进入时,进行判断,需要boolean返回值,如果返回true将继续执行,如果返回false,将不进行执行.一般用于 ...

  6. 好客租房6-React脚手架的应用(细节)

    3.2使用react脚手架初始化 npx命令介绍 npm v5.2.0引入的一条命令 目的:提升包内提供的命令行工具的使用体验 原先:先安装脚手架包 再使用这个包中提供的命令 现在无需安装脚手架包 就 ...

  7. OpenHarmony3.1 Release版本特性解析——硬件资源池化架构介绍

    李刚 OpenHarmony 分布式硬件管理 SIG 成员 华为技术有限公司分布式硬件专家 OpenHarmony 作为面向全场景.全连接.全智能时代的分布式操作系统,通过将各类不同终端设备的能力进行 ...

  8. JS倒计时(刷新页面不影响)的实现思路

    最近在做一个项目,用到了点击按钮实现倒计时,这个用js来实现很简单.但是遇到了一个问题 页面刷新后js重新加载导致 倒计时重新开始,或者直接初始化了 后来通过 cookie 保存来实现了js倒计时,关 ...

  9. 【leetcode 206】 反转链表(简单)

    链表 概念: 区别于数组,链表中的元素不是存储在内存中连续的一片区域,链表中的数据存储在每一个称之为「结点」复合区域里,在每一个结点除了存储数据以外,还保存了到下一个结点的指针(Pointer). 由 ...

  10. MTK 平台sensor arch 介绍-hal

    MTK 平台sensor arch 介绍-hal 一:整体框架 二:具体流程简介 AP-HAL: (1)init & control flow 我们以前文的originchannel 的 ac ...