美文网首页Arnold
基础知识梳理&HashMap扩容机制和加载因子

基础知识梳理&HashMap扩容机制和加载因子

作者: 诺诺诺诺诺诺诺诺诺 | 来源:发表于2019-07-31 14:46 被阅读0次
    public class HashMap<K,V>extends AbstractMap<K,V>implements Map<K,V>, Cloneable, Serializable{
        //  默认的初始容量(容量为HashMap中桶的数目)是16,且实际容量必须是2的整数次幂。 
        static final int DEFAULT_INITIAL_CAPACITY = 16;
        // 最大容量(必须是2的幂且小于2的30次方,传入容量过大将被这个值替换)
        static final int MAXIMUM_CAPACITY = 1 << 30;
        // 默认加载因子
        static final float DEFAULT_LOAD_FACTOR = 0.75f;
        //... 省略
    }
    

    通过以上源码可以看到在源码中定义了一下几个常量:

    • 默认加载因子:这东西说白了就是用来划分整个HashMap容量的百分比,这里默认0.75就是说占用总容量的75%
    • 默认初始容量:如果你不在构造函数中传值,new一个HashMap,他的容量就是2的4次方(16),并且增长也得是2的整数次方(幂)
    • 阀值:首先这个值等于默认加载因子和初始容量的乘机;他的作用是用来预警的,如果HashMap中的容量超过这个阀值了,那就会执行扩容操作,低于则没事

    很多人忽视的加载因子Load Factor

    加载因子存在的原因,还是因为减缓哈希冲突,如果初始桶为16,等到满16个元素才扩容,某些桶里可能就有不止一个元素了。所以加载因子默认为0.75,也就是说大小为16的HashMap,到了第13个元素,就会扩容成32

    考虑加载因子地设定初始大小

    相比扩容时只是System.arraycopy()的ArrayList,HashMap扩容的代价其实蛮大的,首先,要生成一个新的桶数组,然后要把所有元素都重新Hash落桶一次,几乎等于重新执行了一次所有元素的put。
    所以如果你心目中有明确的Map 大小,设定时一定要考虑加载因子的存在。

    建议你在知道你要存储的容量的时候,直接这样定义:

    Map mapBest = new HashMap((int) ((float) 拟存的元素个数 / 0.75F + 1.0F));
    

    这样一次到位,虽然存在些资源浪费,但是比起重新扩容还是效率高很多
    Map map = new HashMap(srcMap.size())这样的写法肯定是不对的,有25%的可能会遇上扩容。
    Thrift里的做法比较粗暴, Map map = new HashMap( 2* srcMap.size()), 直接两倍又有点浪费空间。

    Guava的做法则是加上如下计算

    (int) ((float) expectedSize / 0.75F + 1.0F);
    

    示例:

        static class Demo {
            int id;
            String name;
            public Demo(int id, String name) {
                this.id = id;
                this.name = name;
            }
        }
        static List<Demo> demoList;
        static {
            demoList = new ArrayList();
            for (int i = 0; i < 10000; i ++) {
                demoList.add(new Demo(i, "test"));
            }
        }
        public void test() {
    
            Map map = new HashMap((int)(demoList.size() / 0.75f) + 1);
            for (Demo demo : demoList) {
                map.put(demo.id, demo.name);
            }
        }    
    

    减小加载因子

    在构造函数里,设定加载因子是0.5甚至0.25。
    如果你的Map是一个长期存在而不是每次动态生成的,而里面的key又是没法预估的,那可以适当加大初始大小,同时减少加载因子,降低冲突的机率。毕竟如果是长期存在的map,浪费点数组大小不算啥,降低冲突概率,减少比较的次数更重要。

    相关文章

      网友评论

        本文标题:基础知识梳理&HashMap扩容机制和加载因子

        本文链接:https://www.haomeiwen.com/subject/cfftdctx.html