zoukankan      html  css  js  c++  java
  • Java HashMap 分析之三:放入元素

    现在,有了hash code,来考虑如何计算放入数组的位置。hash code值通常会很大,但是数组的大小有限,默认只有16,大的也不能超过2的30次方。所以,用模运算来保证在数组大小范围内是合理的,比如:index = hash code % array size.不过这有点慢,JDK采用了更快的算法。这个更快的算法源于一个数学规律,就是如果size是2的N次方,那么数X对size的模运算结果等价于X和size-1的按位与运算,也就是 X % size <=> X & (size -1).按位与只消耗一个CPU周期,当然快多了。现在就可理解为什么要故意把数组大小弄成2的N次方了。再回头看一开始计算数组大小的代码,完全理解了。

    1. int capacity = 1;  
    2. while (capacity < initialCapacity)  
    3.       capacity <<= 1;  


    比如size=16,二进制表示如下:(32位)
    0000000000000000000000000010000
    size-1=15,表示如下:
    0000000000000000000000000001111

    假如hash code=4
    0000000000000000000000000000100
    4 & 15 结果为:
    0000000000000000000000000000100

    假如hash code=6
    0000000000000000000000000000101
    6 & 15 结果为:
    0000000000000000000000000000101

    假如hash code=38
    0000000000000000000000000100110
    38 & 15 结果为:
    0000000000000000000000000000110

    通过观察这三个例子,又可以发现一个特点,也就是X & size-1 的结果受到了size的阶数的限制,这里size=16,阶数为4.结果就是只用低4位的1和X按位与,而X的高位没有用到。这会导致重复率相当高。如果用一个算法将X的低位重新计算,比如根据所有位的值进行重新计算,就可以使得hash值分布更均匀。下面的代码揭示了在真正按位与之前,调用了hash函数,进行了一堆位运算。至于为什么用这个算法,我也不知道其来历。

    1. public V put(K key, V value) {  
    2.         if (key == null)  
    3.             return putForNullKey(value);  
    4.         int hash = hash(key.hashCode());  
    5.         int i = indexFor(hash, table.length);  
    6.         for (Entry<K,V> e = table[i]; e != null; e = e.next) {  
    7.             Object k;  
    8.             if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {  
    9.                 V oldValue = e.value;  
    10.                 e.value = value;  
    11.                 e.recordAccess(this);  
    12.                 return oldValue;  
    13.             }  
    14.         }  
    15.   
    16.         modCount++;  
    17.         addEntry(hash, key, value, i);  
    18.         return null;  
    19.     }  
    20.   
    21.     static int hash(int h) {  
    22.         // This function ensures that hashCodes that differ only by  
    23.         // constant multiples at each bit position have a bounded  
    24.         // number of collisions (approximately 8 at default load factor).  
    25.         h ^= (h >>> 20) ^ (h >>> 12);  
    26.         return h ^ (h >>> 7) ^ (h >>> 4);  
    27.     }  
    28.   
    29.     static int indexFor(int h, int length) {  
    30.         return h & (length-1);  
    31.     }  
    32.   
    33.     void addEntry(int hash, K key, V value, int bucketIndex) {  
    34.         Entry<K,V> e = table[bucketIndex];  
    35.         table[bucketIndex] = new Entry<K,V>(hash, key, value, e);  
    36.         if (size++ >= threshold)  
    37.             resize(2 * table.length);  
    38.     }  


    上面的for循环是查找并替换符合条件的对象,如果找不到,则添加新的对象。查找到的条件(必须都满足)是:
    1.hash值相等
    2.key的引用相同或者key的值相等。

  • 相关阅读:
    Elasticsearch(ES) 创建索引
    Elasticsearch(ES) 下载&安装
    一文带您了解 Elasticsearch 中,如何进行索引管理(图文教程)
    Spring Boot 2.0 快速集成整合消息中间件 Kafka
    一文教您如何通过 Docker 搭建反向代理 Ngnix,并配置 Https SSL 证书
    Django开发文档-域用户集成登录
    Python实现按键精灵(二)-找图找色
    Python学习笔记-SQLSERVER的大批量导入以及日常操作(比executemany快3倍)
    Python爬虫案例-获取最新的中国行政区域划分
    PostgreSQL自动更新序列sequence
  • 原文地址:https://www.cnblogs.com/chenying99/p/3120347.html
Copyright © 2011-2022 走看看