• 当添加一个键值对元素时,HashMap发生了什么?


    1,我们先用一张思维导图来了解一下HashMap中的put()过程

     

     2,HashMap数据结构

                HashMap内部数据结构采用数组+链表+红黑树进行存储。数组类型为Noded[ ],每个Node对象都保存了某个KV键值对元素的key,value,hash,next的值。

    1. public class HashMap{
    2. // 每个Node既保存一个KV键值对,同时也是链表中的一个节点
    3. Node<K,V>[] table;
    4. }

    链表节点类:

    1. public class HashMap{
    2. // ...
    3. // 静态内部类Node
    4. static class Node<K,V> implements Map.Entry<K,V> {
    5. final int hash; // 哈希值
    6. final K key; // 键
    7. V value; // 值
    8. Node next; // 下一个节点(由于只拥有next,所以该链表为单向链表)
    9. }
    10. // ...
    11. }

    3,hashMap的初始容量

    默认数组大小为16,下面是源码部分:

    1. public class HashMap{
    2. /**
    3. * The default initial capacity - MUST be a power of two.
    4. */
    5. static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
    6. // 添加键值对
    7. final V putVal(int hash, K key, V value) {
    8. Node[] tab; // 数组
    9. Node p; int n, i;
    10. if ((tab = table) == null || (n = tab.length) == 0)
    11. // 如果数组为空,则该数组扩容为16
    12. n = (tab = resize()).length;
    13. }
    14. }

    4,hashMap扩容

       由于底层采用数组+链表+红黑树,扩容过程中需要按照数组容量和加载因子来进行判断。

      数组容量:基础数组Node [ ]  table 的长度,如果没有指定容量,添加第一个元素时,默认容量为16。

      加载因子:用来表示HashMap集合中的元素的填满程度,默认为0.75.越大则表示允许填满的元素就越多,集合的空间利用率就越高,但是冲突的机会就会增加。反之,如果太小,空间的利用率就低。

    下面是HashMap的源代码解析:

    1. public class HashMap{
    2. // 默认链表长度
    3. static final int TREEIFY_THRESHOLD = 8;
    4. // 添加键值对的方法
    5. final V putVal(int hash, K key, V value){
    6. //...
    7. // 将key和value,插入链表尾部
    8. // 循环时,使用binCount进行计数
    9. for (int binCount = 0; ; ++binCount) {
    10. // 判断节点p的next是否空
    11. if ((e = p.next) == null) {
    12. // 插入链表尾部
    13. p.next = newNode(hash, key, value, null);
    14. // 判断链表数量是否大于8
    15. if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
    16. // 链表长度超过8,将当前链表转换为红黑树
    17. treeifyBin(tab, hash);
    18. break;
    19. }
    20. }
    21. }
    22. // 转换为红黑树
    23. final void treeifyBin(Node[] tab, int hash) {
    24. int n, index; Node e;
    25. // 数组长度如果小于64,则优先扩容数组
    26. if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
    27. resize();
    28. else if ((e = tab[index = (n - 1) & hash]) != null) {
    29. // 遍历链表节点,转换为红黑树
    30. TreeNode hd = null, tl = null;
    31. do {
    32. TreeNode p = replacementTreeNode(e, null);
    33. if (tl == null)
    34. hd = p;
    35. else {
    36. p.prev = tl;
    37. tl.next = p;
    38. }
    39. tl = p;
    40. } while ((e = e.next) != null);
    41. if ((tab[index] = hd) != null)
    42. hd.treeify(tab);
    43. }
    44. }
    45. }

  • 相关阅读:
    Locust、jmeter、Loadrunner三种工具的分布式压测
    蓝桥杯 (饮料换购,C++)
    Flex布局下文本溢出省略号失效了,Flex自适应宽度显示省略号
    hadoop dfsadmin -refreshNodes 命令详解
    18.JVM
    你知道防抖和节流与闭包的关系吗?
    Tomcat 日志乱码问题解决
    【Linux】Ubuntu 压缩与解压缩
    ChatGLM3-6B:新一代开源双语对话语言模型,流畅对话与低部署门槛再升级
    思科对路由器的配置
  • 原文地址:https://blog.csdn.net/weixin_54535063/article/details/126338627