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);
    }
时间: 2024-08-25 20:32:25

HashMap源码分析三的相关文章

HashMap源码分析(转载)

一.HashMap概述 HashMap基于哈希表的 Map 接口的实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.(除了不同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同.)此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap. Map map = Coll

Java集合系列之HashMap源码分析

一.HashMap简介 HashMap是基于哈希表的Map接口实现的,它存储的是内容是键值对<key,value>映射.此类不保证映射的顺序,假定哈希函数将元素适当的分布在各桶之间,可为基本操作(get和put)提供稳定的性能. ps:本文中的源码来自jdk1.8.0_45/src. 1.重要参数 HashMap的实例有两个参数影响其性能. 初始容量:哈希表中桶的数量 加载因子:哈希表在其容量自动增加之前可以达到多满的一种尺度 当哈希表中条目数超出了当前容量*加载因子(其实就是HashMap的

Java集合之HashMap源码分析

一.HashMap简介 HashMap是基于哈希表的Map接口实现的,它存储的是内容是键值对<key,value>映射.此类不保证映射的顺序,假定哈希函数将元素适当的分布在各桶之间,可为基本操作(get和put)提供稳定的性能. ps:本文中的源码来自jdk1.8.0_45/src. 1.重要参数 HashMap的实例有两个参数影响其性能. 初始容量:哈希表中桶的数量 加载因子:哈希表在其容量自动增加之前可以达到多满的一种尺度 当哈希表中条目数超出了当前容量*加载因子(其实就是HashMap的

Java中HashMap源码分析

一.HashMap概述 HashMap基于哈希表的Map接口的实现.此实现提供所有可选的映射操作,并允许使用null值和null键.(除了不同步和允许使用null之外,HashMap类与Hashtable大致相同)此类不保证映射的顺序,特别是它不保证该顺序恒久不变. 值得注意的是HashMap不是线程安全的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap. Map map = Collections.sync

Java BAT大型公司面试必考技能视频-1.HashMap源码分析与实现

视频通过以下四个方面介绍了HASHMAP的内容 一. 什么是HashMap Hash散列将一个任意的长度通过某种算法(Hash函数算法)转换成一个固定的值. MAP:地图 x,y 存储 总结:通过HASH出来的值,然后通过值定位到这个MAP,然后value存储到这个MAP中的HASHMAP基本原理 1. KEY 是否可以为空?可以,Null当成一个Key来存储 2. 如果Hash KEY重复了会覆盖吗?会覆盖,但返回旧的值 3. HASHMAP什么时候做扩容?put 的时候,阀值高于或等于0.7

[Java] HashMap源码分析

1.概述 Hashmap继承于AbstractMap,实现了Map.Cloneable.java.io.Serializable接口.它的key.value都可以为null,映射不是有序的. Hashmap不是同步的,如果想要线程安全的HashMap,可以通过Collections类的静态方法synchronizedMap获得线程安全的HashMap. Map map = Collections.synchronizedMap(new HashMap()); (除了不同步和允许使用 null 之

【JAVA集合】HashMap源码分析(转载)

原文出处:http://www.cnblogs.com/chenpi/p/5280304.html 以下内容基于jdk1.7.0_79源码: 什么是HashMap 基于哈希表的一个Map接口实现,存储的对象是一个键值对对象(Entry<K,V>): HashMap补充说明 基于数组和链表实现,内部维护着一个数组table,该数组保存着每个链表的表头结点:查找时,先通过hash函数计算hash值,再根据hash值计算数组索引,然后根据索引找到链表表头结点,然后遍历查找该链表: HashMap数据

Nouveau源码分析(三):NVIDIA设备初始化之nouveau_drm_probe

Nouveau源码分析(三) 向DRM注册了Nouveau驱动之后,内核中的PCI模块就会扫描所有没有对应驱动的设备,然后和nouveau_drm_pci_table对照. 对于匹配的设备,PCI模块就调用对应的probe函数,也就是nouveau_drm_probe. // /drivers/gpu/drm/nouveau/nouveau_drm.c 281 static int nouveau_drm_probe(struct pci_dev *pdev, 282 const struct

[Android]Fragment源码分析(三) 事务

Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的非常的出彩.我们先引入一个简单常用的Fragment事务管理代码片段: FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction(); ft.add(R.id.fragmentContainer, fragment, "tag"); ft.addToBackStack("<span style="fo