zoukankan      html  css  js  c++  java
  • java基础-HashMap

    jdk7的HashMap实现的思路比较简单,就是一个Entry数组,数组中每个Entry都是一个链表的起点(表头)。

     1     public V put(K key, V value) {
     2         if (table == EMPTY_TABLE) {
     3             inflateTable(threshold);
     4         }
     5         //如果key为null,则将该entry放在第0位
     6         if (key == null)
     7             return putForNullKey(value);
     8         int hash = hash(key);
     9         int i = indexFor(hash, table.length);
    10         //检查第i位的链表中是否存在该key,如果存在,则将原value替换为新value
    11         for (Entry<K,V> e = table[i]; e != null; e = e.next) {
    12             Object k;
    13             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
    14                 V oldValue = e.value;
    15                 e.value = value;
    16                 e.recordAccess(this);
    17                 //并返回旧value
    18                 return oldValue;
    19             }
    20         }
    21 
    22         modCount++;
    23         //如果不存在则将entry添加在第i位链表表头,如果(size >= threshold) && (null != table[bucketIndex])为真(threshold=capacity*loadFactor),还会涉及resize(扩容,resize(2 * table.length);)操作
    24         addEntry(hash, key, value, i);
    25         return null;
    26     }

     jdk7的hash函数

     1     final int hash(Object k) {
     2         int h = hashSeed;
     3         if (0 != h && k instanceof String) {
     4             return sun.misc.Hashing.stringHash32((String) k);
     5         }
     6 
     7         h ^= k.hashCode();
     8 
     9         // This function ensures that hashCodes that differ only by
    10         // constant multiples at each bit position have a bounded
    11         // number of collisions (approximately 8 at default load factor).
    12         h ^= (h >>> 20) ^ (h >>> 12);
    13         return h ^ (h >>> 7) ^ (h >>> 4);
    14     }

    jdk7中的HashMap存在一个问题,如果key的hash值都映射到同一个桶中,hashMap的查找就会退化成顺序查找,这会极大影响查找性能(对插入性能无影响)。jdk8为了解决这一问题,对HashMap进行了一些改进,当一个桶中的链表长度大于内设阈值(8)时,就会将该桶的链表树化(treeify),即将链表转变为一个红黑树(其余位置的链表不会受到影响)。

    1     public V put(K key, V value) {
    2         return putVal(hash(key), key, value, false, true);
    3     }

     jdk8的hash函数

    1     static final int hash(Object key) {
    2         int h;
    3         //hashCode返回一个int共32位,最终就是高16位保持原样,低16位与高16亦或后形成新的低16位。这样做是为了防止hashCode低位全0的情况,在映射后(hash&(table.length-1))就会聚集在第0位。
    4         return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    5     }
     1     final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
     2                    boolean evict) {
     3         //tab就是table,p是前驱节点,n是table长,i是key的index
     4         Node<K,V>[] tab; Node<K,V> p; int n, i;
     5         if ((tab = table) == null || (n = tab.length) == 0)
     6             n = (tab = resize()).length;
     7         //如果第i位尚没有节点,则直接插入
     8         if ((p = tab[i = (n - 1) & hash]) == null)
     9             tab[i] = newNode(hash, key, value, null);
    10         else {//第i位已经存在节点的情况
    11             Node<K,V> e; K k;
    12             //首节点的key与即将插入的key相同
    13             if (p.hash == hash &&
    14                 ((k = p.key) == key || (key != null && key.equals(k))))
    15                 e = p;
    16             //如果首节点是TreeNode,说明该位是一棵红黑树,直接插入
    17             else if (p instanceof TreeNode)
    18                 e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
    19             //遍历链表进行替换或插入
    20             else {
    21                 for (int binCount = 0; ; ++binCount) {
    22                 //如果p.next为null,则插入到链表末端
    23                 if ((e = p.next) == null) {
    24                         p.next = newNode(hash, key, value, null);
    25                         //如果链表长度大于TREEIFY_THRESHOLD(8,不可设置)则树化
    26                         if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
    27                             treeifyBin(tab, hash);//树化该位的链表为一棵红黑树
    28                         break;
    29                     }
    30                     //如果找到相同的key,则直接跳出,后续会更新value
    31                     if (e.hash == hash &&
    32                         ((k = e.key) == key || (key != null && key.equals(k))))
    33                         break;
    34                     p = e;
    35                 }
    36             }
    37             //更新value
    38             if (e != null) { // existing mapping for key
    39                 V oldValue = e.value;
    40                 if (!onlyIfAbsent || oldValue == null)
    41                     e.value = value;
    42                 afterNodeAccess(e);
    43                 return oldValue;
    44             }
    45         }
    46         ++modCount;
    47         if (++size > threshold)
    48             resize();
    49         afterNodeInsertion(evict);
    50         return null;
    51     }
  • 相关阅读:
    运行出现Server Tomcat v8.5 Server at localhost failed to start.和A child container failed during start
    com.microsoft.sqlserver.jdbc.SQLServerException: Socket closed 或者 该连接已关闭
    java反射 反射构造函数 报 wrong number of arguments 错误
    视高盛景企业级移动应用解决方案 让一次开发实现多平台应用
    你知道现在的.net是什么样的吗,一张图告诉你
    Azure 项目构建 – 构建直播教学系统之媒体服务篇
    Azure 项目构建 – 构建和部署 .NET 应用程序
    基于 Azure IaaS 搭建企业官网的规划和实践
    这么大一座Azure“图书馆”,你竟没有发现…
    Azure 进阶攻略 | 关于Java 和事件中心的那不得不说的事
  • 原文地址:https://www.cnblogs.com/holoyong/p/7259734.html
Copyright © 2011-2022 走看看