集合分析HashMap--CurrentHashMap

HashMap的内部存储结构

哈希表:链表+数组, 链表上挂载的值对应着地址值,在堆里能找到这个值。
数组:查询快,增删慢。(详情参见数据结构与算法)
链表:查询慢,增删快。
结构图:根据每个对象的哈希值决定对应的存储位置–value%数组长度=存放位置。
这里存在一个问题:数组扩容怎么处理?
理论上来说,尽量不要走扩容,非常消耗性能,正如我们所想的,rehash,数据复制。。
关于在什么时候会自动扩容–这个要看源码,
hash(Object key)

看源码

1
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
初始容量,使用到了只面试里面见过的左移
难点:TREEIFY_THRESHOLD 用于判断是否需要将链表转换为红黑树的阈值。
使用: if (binCount >= TREEIFY_THRESHOLD - 1) // 在将链表转为红黑树,提升查询效率
2
if (loadFactor <= 0 || Float.isNaN(loadFactor))
关于NaN,在除以0时会出现,显然这里会传入nan
3
public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);   
这里先无符号右移16位,再取异或---目的是让结果更加有随机性,具体原理比较复杂,可以参考上面的链接。
4
if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
没有括号,当然,我知道,但从来没这么用过。
5
first = tab[(n - 1) & hash]) != null
内核跟取余相似,实质是内存级的位运算取余,但有限定条件。
6
if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
在jdk1.8里做了红黑树的优化,这里就是算法上的优化,调用get方法时,判断是不是链表,红黑树。
查询效率:查询效率从 O(n)  提高到了 O(logn)     
7
线程安全的分析:
线程安全问题出现在扩容时,调用resize(),rehash()方法时,大致的原因就是形成了环形链表。
         

CurrentHashMap

最关键的HashEntry--虽然名称在变,但本质都是entry:这里有两个volatile修饰的字段,但是还是不能保证其原子性
static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        volatile V val;
        volatile Node<K,V> next;
原子性:

发布了25 篇原创文章 · 获赞 0 · 访问量 565

猜你喜欢

转载自blog.csdn.net/weixin_43343786/article/details/104003569