• 源码分析八( hashmap工作原理)


    首先从一条简单的语句开始,创建了一个hashmap对象:

     Map<String,String> hashmap = new HashMap<String,String>(); 

    调用hashmap类无参数构造方法,使用默认转载因子,

    1 public HashMap() {
    2         this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    3     }

    默认装载因子只有在构造器不指定的情况下使用

    1 /**
    2      * The load factor used when none specified in constructor.
    3      */
    4     static final float DEFAULT_LOAD_FACTOR = 0.75f;

    hashmap还有三个重载的构造方法,分别是:

    有装载因子入参的构造方法

    1 public HashMap(int initialCapacity) {
    2         this(initialCapacity, DEFAULT_LOAD_FACTOR);
    3     }

    有初始化容器容量以及装载因子的构造方法

     1 public HashMap(int initialCapacity, float loadFactor) {
     2         //如果初始容量小于0,则抛出非法初始化容量异常
     3         if (initialCapacity < 0)
     4             throw new IllegalArgumentException("Illegal initial capacity: " +
     5                                                initialCapacity);
     6         //如果初始容量大于最多容量,则容量为最多容量
     7         if (initialCapacity > MAXIMUM_CAPACITY)
     8             initialCapacity = MAXIMUM_CAPACITY;
     9         //如果装载因子不大于0或者装载因子不是数字,则抛出装载因子非法异常
    10         if (loadFactor <= 0 || Float.isNaN(loadFactor))
    11             throw new IllegalArgumentException("Illegal load factor: " +
    12                                                loadFactor);
    13         this.loadFactor = loadFactor;
    14         //根据初始化容量计算hashmap的阈值容量(hashmap真正的容量,必须是2的次方)
    15         this.threshold = tableSizeFor(initialCapacity);
    16      }

    计算hashmap的容量

     1 // 根据给定的目标容量,计算一个2的次方
     2     static final int tableSizeFor(int cap) {
     3         int n = cap - 1;
     4         n |= n >>> 1;
     5         n |= n >>> 2;
     6         n |= n >>> 4;
     7         n |= n >>> 8;
     8         n |= n >>> 16;
     9         return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
    10     }

    有参数map的构造方法:

    1 public HashMap(Map<? extends K, ? extends V> m) {
    2             this.loadFactor = DEFAULT_LOAD_FACTOR;
    3             putMapEntries(m, false);
    4      }

    将map复制一份到本地,这个复制如果对于数值型则为数值,对于引用型则为地址

     1 final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
     2         int s = m.size();
     3         //原来map的长度大于0处理
     4         if (s > 0) {
     5             if (table == null) { // pre-size
     6                 //根据实际数量计算需要多大的容量
     7                 float ft = ((float) s / loadFactor) + 1.0F;
     8                 int t = ((ft < (float) MAXIMUM_CAPACITY) ? (int) ft : MAXIMUM_CAPACITY);
     9                 //如果预估容量大于现有容量,则需要根据预估容量计算一个2的次方数
    10                 if (t > threshold)
    11                     threshold = tableSizeFor(t);
    12             }
    13             //如果实际容量大于阈值则需要扩容
    14             else if (s > threshold)
    15                 resize();
    16             //扩容之后,将m的key和value复制一份到本地
    17             for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
    18                 K key = e.getKey();
    19                 V value = e.getValue();
    20                 putVal(hash(key), key, value, false, evict);
    21             }
    22         }
    23     }
     1 //第一次调用初始化或者扩容
     2     final Node<K,V>[] resize() {
     3         //首次调用时为null,则oldCap为0 | 扩容时,
     4         Node<K,V>[] oldTab = table;
     5         int oldCap = (oldTab == null) ? 0 : oldTab.length;
     6         int oldThr = threshold;
     7         int newCap, newThr = 0;
     8         //扩容时,如果目前容量已经超过最多容量,那么默认值为int最大,否则容量为现在2倍
     9         if (oldCap > 0) {
    10             if (oldCap >= MAXIMUM_CAPACITY) {
    11                 threshold = Integer.MAX_VALUE;
    12                 return oldTab;
    13             }
    14             else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
    15                      oldCap >= DEFAULT_INITIAL_CAPACITY)
    16                 newThr = oldThr << 1; // double threshold
    17         }
    18         //初始化时newCap = oldThr = threshold
    19         else if (oldThr > 0) // initial capacity was placed in threshold
    20             newCap = oldThr;
    21         else {               // zero initial threshold signifies using defaults
    22             newCap = DEFAULT_INITIAL_CAPACITY;
    23             newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    24         }
    25         //初始化时newThr=newCap * loadFactor
    26         if (newThr == 0) {
    27             float ft = (float)newCap * loadFactor;
    28             newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
    29                       (int)ft : Integer.MAX_VALUE);
    30         }
    31         threshold = newThr;
    32         @SuppressWarnings({"rawtypes","unchecked"})
    33         //初始化时,创建一个长度为threshold的Node数组,然后返回
    34             Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    35         table = newTab;
    36         if (oldTab != null) {
    37             for (int j = 0; j < oldCap; ++j) {
    38                 Node<K,V> e;
    39                 if ((e = oldTab[j]) != null) {
    40                     oldTab[j] = null;
    41                     if (e.next == null)
    42                         newTab[e.hash & (newCap - 1)] = e;
    43                     else if (e instanceof TreeNode)
    44                         ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
    45                     else { // preserve order
    46                         Node<K,V> loHead = null, loTail = null;
    47                         Node<K,V> hiHead = null, hiTail = null;
    48                         Node<K,V> next;
    49                         do {
    50                             next = e.next;
    51                             if ((e.hash & oldCap) == 0) {
    52                                 if (loTail == null)
    53                                     loHead = e;
    54                                 else
    55                                     loTail.next = e;
    56                                 loTail = e;
    57                             }
    58                             else {
    59                                 if (hiTail == null)
    60                                     hiHead = e;
    61                                 else
    62                                     hiTail.next = e;
    63                                 hiTail = e;
    64                             }
    65                         } while ((e = next) != null);
    66                         if (loTail != null) {
    67                             loTail.next = null;
    68                             newTab[j] = loHead;
    69                         }
    70                         if (hiTail != null) {
    71                             hiTail.next = null;
    72                             newTab[j + oldCap] = hiHead;
    73                         }
    74                     }
    75                 }
    76             }
    77         }
    78         return newTab;
    79     }
  • 相关阅读:
    SCXML和QScxml使用总结
    qt 使用qtxlsx 读写excel
    Qt Qml嵌入Widget以及Qml与Widget交互
    三步带你开发一个短链接生成平台
    SessionStorage、LocalStorage详解
    低代码如何支撑企业级应用开发?
    开发一个渐进式Web应用程序(PWA)前都需要了解什么?
    详细了解JS Map,它和传统对象有什么区别?
    5种可能在10年后消失的开发语言
    更改tomcat启动的端口号
  • 原文地址:https://www.cnblogs.com/warrior4236/p/9135394.html
Copyright © 2020-2023  润新知