• HashMap源码分析三


     
    HashMap的源码,在jdk1.5中相比jdk1.4,改动不大,有几个方面
     
    1 jdk1.5中引入了范型,在HashMap中也有体现
    2 引入了另一个hash值的计算方式,不过默认是关闭状态,可以通过设置jvm的参数开启
     
    private static int oldHash(int h) {
            h += ~(h << 9);
            h ^=  (h >>> 14);
            h +=  (h << 4);
            h ^=  (h >>> 10);
            return h;
        }
     
        private static int newHash(int h) {
     
            h ^= (h >>> 20) ^ (h >>> 12);
            return h ^ (h >>> 7) ^ (h >>> 4);
        }
     
        static int hash(int h) {
            return useNewHash ? newHash(h) : oldHash(h);
        }
        // -XX:+UseNewHashFunction or -XX:+AggressiveOpts 启动新的hash计算
        private static final boolean useNewHash;
        static { 
            useNewHash = false; 
        }
     
    3 代码的小优化
     
    public V get(Object key) {
            // key为null时,取value的过程抽离到一个方法里
            if (key == null)
                return getForNullKey();
            int hash = hash(key.hashCode());
            for (Entry<K,V> e = table[indexFor(hash, table.length)];
                 e != null;
                 e = e.next) {
                    Object k;
                    if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                        return e.value;
            }
            return null;
    }
     
    HashMap的源码,在jdk1.6中相比于jdk1.5也没什么改动,在这个版本开始,开始启用新的hash计算方式,把以前的废弃了
     
    static int hash(int h) {
     
            h ^= (h >>> 20) ^ (h >>> 12);
            return h ^ (h >>> 7) ^ (h >>> 4);
    }
     
    在jdk1.7中,相比与jdk1.6,变动也不是很大。
     
    在jdk1.7中,HashMap的初始数组长度,没有设置了。可能出于内存有效使用的考虑。
       // 构造方法里并没有初始化数组
        public HashMap(int initialCapacity, float loadFactor) {
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal initial capacity: " +
                                                   initialCapacity);
            if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new IllegalArgumentException("Illegal load factor: " +
                                                   loadFactor);
     
            this.loadFactor = loadFactor;
            // 数组增长阀值,先设置成数组的初始长度
            threshold = initialCapacity;
            init();
        }
     
     
         public V put(K key, V value) {
            // 如果数组没有初始化,开始初始化
            if (table == EMPTY_TABLE) {
                inflateTable(threshold);
            }
            ....
        }
     
    这里引入了一个新方法inflateTable,初始化数组 
        // 初始化数组
         private void inflateTable(int toSize) {
            // 计算确认初始化数组的长度
            int capacity = roundUpToPowerOf2(toSize);
            // 重新给数组长度的阀值赋值
            threshold = (int) Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
            table = new Entry[capacity];
            // 这里是判断是否需要给hash值生成添加生成因子,减少hash碰撞的概率
            initHashSeedAsNeeded(capacity);
        }
     
        // 获得初始化数组的长度
        private static int roundUpToPowerOf2(int number) {
            // 分几种情况
            // 1 number超过数组长度限制最大值,则返回数组长度的最大值
            // 2 number小于或者等于1, 则返回1
            // 3 否则返回Integer.highestOneBit((number - 1) << 1),这个表示number二进制表示时,最左边位数的值为1,其他位为0时的值
            // 比如 0000 0000 0000 0000 1011 0011 0000 0000 计算后值为 0000 0000 0000 0000 1000 0000 0000 0000
            return number >= MAXIMUM_CAPACITY
                    ? MAXIMUM_CAPACITY
                    : (number > 1) ? Integer.highestOneBit((number - 1) << 1) : 1;
        }
     
        // 初始化是否使用hash值生成的生成因子
        final boolean initHashSeedAsNeeded(int capacity) {
            // 判断是否已经使用了生成因子,hashSeed=0表示未使用
            boolean currentAltHashing = hashSeed != 0;
            // 判断是否使用生成因子
            boolean useAltHashing = sun.misc.VM.isBooted() &&
                    (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
            boolean switching = currentAltHashing ^ useAltHashing;
            if (switching) {
                hashSeed = useAltHashing
                    ? sun.misc.Hashing.randomHashSeed(this)
                    : 0;
            }
            return switching;
        }
     
    这里在看下hash值的生成
     
       final int hash(Object k) {
            int h = hashSeed;
            // 如果key对象是字符串,则换个方式生成hash值
            if (0 != h && k instanceof String) {
                return sun.misc.Hashing.stringHash32((String) k);
            }
            // 添加生成因子
            h ^= k.hashCode();
     
            // This function ensures that hashCodes that differ only by
            // constant multiples at each bit position have a bounded
            // number of collisions (approximately 8 at default load factor).
            h ^= (h >>> 20) ^ (h >>> 12);
            return h ^ (h >>> 7) ^ (h >>> 4);
        }
     
    上面提到一个方法Integer.highestOneBit,可能看着挺困惑的,这个是Integer里的一个方法。
        // 或者i最高位为1,其他位为0时的值
        // 如:0000 0000 0000 0000 1011 0011 0000 0000 计算后值为 0000 0000 0000 0000 1000 0000 0000 0000
        public static int highestOneBit(int i) {
            // 开始位运算
            i |= (i >>  1);// i左移1位,在和i值或运算,得到值的最高位和第2位都为1 (如果最高为到最右端不到2位,则最高位到最右端都为1)
            i |= (i >>  2);// 新的i左移2位,在和i值或运算,得到值的最高位到第4位都为1 (如果最高为到最右端不到4位,则最高位到最右端都为1)
            i |= (i >>  4);// 新的i左移4位,在和i值或运算,得到值的最高位到第8位都为1 ( 如果最高为到最右端不到8位,则最高位到最右端都为1)
            i |= (i >>  8);// 新的i左移8位,在和i值或运算,得到值的最高位到第16位都为1 (如果最高为到最右端不到16位,则最高位到最右端都为1)
            i |= (i >> 16);// 新的i左移16位,在和i值或运算,得到值的最高位到第32位都为1 (如果最高为到最右端不到32位,则最高位到最右端都为1)
            // 非最高位设置为0
            return i - (i >>> 1);
        }
     
     
  • 相关阅读:
    Oracle OMF 创建数据库
    Oracle 数据库修改数据文件
    oracle 12c 安装文档
    mysql5.7 多次增量备份及恢复测试
    Mysql 数据库模拟误操作基于binlog恢复
    基于scn号恢复oracle数据库测试
    读源码 | metisMenu侧边栏插件
    JavaScript | JQuery插件定义方法
    JavaScript | 模拟文件拖选框样式 v1.0
    JavaScript | JSON基本格式
  • 原文地址:https://www.cnblogs.com/sten/p/5720525.html
Copyright © 2020-2023  润新知