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

HashMap的加载因子默认为0.75,当达到阈值时会触发扩容。加载因子的存在是为了减缓哈希冲突,若已知Map大小,初始化时应考虑加载因子以避免扩容带来的性能损失。Guava推荐的初始化方式是根据实际大小计算,以降低冲突概率。减小加载因子可以减少比较次数,但会增加内存消耗。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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,浪费点数组大小不算啥,降低冲突概率,减少比较的次数更重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值