美文网首页读书
模拟HashMap冲突

模拟HashMap冲突

作者: Java编程日记 | 来源:发表于2022-07-06 14:34 被阅读0次

最近看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码 :

image.png

遍历 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;
}

调式:

image.png

会一直遍历 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冲突

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

  • 模拟HashMap冲突

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

  • HashMap

    HashMap 解决Hash冲突 java 中的HashMap 通过链表法解决Hash冲突 链表法 链表法就是将相...

  • mysql面试

    Hashmap如果避免hash冲突?

  • HashMap 如何解决冲突,扩容机制

    HashMap 如何解决冲突,扩容机制 我们来看看HashMap的put数据的时候,是怎么处理的: 计算HashC...

  • 建议收藏!细说HashMap实现,Hash冲突模拟思路讲解。

    思路记录: 1.HashMap主体是一个Entry数组,当多个key值经过hash计算得到的索引一致,则发生Has...

  • 从hashmap说起

    说到hashmap我们能想到什么呢 hash hashmap的hash方法极大的避免了hash冲突。他通过高16位...

  • JDK1.8的HashMap源码分析

    JDK1.8之前的HashMap 在JDK1.8之前,HashMap通过散列表(哈希表)实现,并且散列表冲突解决方...

  • 面试的问题

    java集合框架: 1:介绍一下java的集合框架 2:HashMap遇见哈希冲突会如何怎么办?HashMap是线...

  • iOS NSDictionary(字典)~实现原理

    总结:简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的...

网友评论

    本文标题:模拟HashMap冲突

    本文链接:https://www.haomeiwen.com/subject/nhjovrtx.html