HashMap 使用的时候指定容量?你真的用明白了吗?(值得一阅)

简介: HashMap 使用的时候指定容量?你真的用明白了吗?(值得一阅)

前言



其实可以看到我写了这么久的博客,很少去写hashMap的东西。


为什么? 因为这个东西感觉是java面试必备的,我感觉大家都看到腻了,所以一直没怎么去写hashMap相关的。


ps: 之前整理过一个hashmap存值的流程图,感觉够了,因为put过程基本可以把所有核心点都过一遍。JAVA jdk1.8 HashMap 存值流程图解_小目标青年的博客-CSDN博客


今天为什么我突然要来写这一篇文章,因为最近在公司看一些老项目代码,我才发现原来其实很多人都没用对。


本篇内容:


举例说明 HashMap 使用的时候指定容量 错误用法;


源码走读,HashMap初始容量的 计算方式;


源码走读扩容的点;


正确应该怎么去用,一定要理解再用;


一些杂谈。


正文



不开玩笑,真的都知道指定容量,但是有些用对了,有些没用对。


image.png


为什么要指定容量?


这个原由,都不用说,阿里的java开发手册就说的很明白:


image.png


其实核心点,就是避免数据量慢慢增加,导致反复触发扩容,影响性能。


于是乎就很多错误的使用方式了(虽热影响不大):


错误理解使用示例 ① :


分页查询出来的数据,需要转换成 Map, 因为分页是固定了一页最多15条。


所以出现了这个代码:


Map<String, String> map = new HashMap<>(15);

或者是

Map<String, String> map = new HashMap<>(userPageList.size());


错误理解使用示例 ② :


类型type 有 4种, 要放到一个map里面,返回去。


所以出现了这个代码:


Map<Integer, String> map = new HashMap<>(4);


错误理解使用示例 ③:


一个参数map,里面想放2个参数。


所以出现了这个代码:

Map<String, String> map = new HashMap<>(2);


不多举例,其实这几个错误示例,都是错在指定容量的 值上。


默认 指定是 传入 16, 16* 0.75=12 , 所以扩容阈值是12 。


说到这里,大家应该知道为什么上面是错误用法了吧?


比如我们想 存 4个元素到Map, 我们为了避免后面触发扩容影响性能(其实元素少性能没多少影响), 就指定了 4 :


Map<Integer, String> map = new HashMap<>(4);


其实这样 4x0.75= 3 ,那么如果存放第四个元素的时候,就会触发扩容


(JAVA jdk1.8 HashMap 存值流程图解_小目标青年的博客-CSDN博客)image.png

这样就是违背了我们开始指定 的 4 的最初用意。

实战看看这个错误使用场景的情况:

 

同过反射,将capacity属性的权限拿到,可以直接打印出来看下capacity的变化,就知道是否触发了扩容:


    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Map<String, String> map = new HashMap<>(4);
        Class<?> mapType = map.getClass();
        Method capacity = mapType.getDeclaredMethod("capacity");
        capacity.setAccessible(true);
        map.put("1", "第一个元素插入");
        System.out.println("capacity : " + capacity.invoke(map) + "    size : " + map.size());
        map.put("2", "第二个元素插入");
        System.out.println("capacity : " + capacity.invoke(map) + "    size : " + map.size());
        map.put("3", "第三个元素插入");
        System.out.println("capacity : " + capacity.invoke(map) + "    size : " + map.size());
        map.put("4", "第四个元素插入");
        System.out.println("capacity : " + capacity.invoke(map) + "    size : " + map.size());
    }


看下打印效果:


image.png


为什么,当size =3 ,也就是插入三个元素的时候还没变。


因为我们初始化容量值传入的 4,  4* 0.75 =3. 扩容阈值是 3!  


image.png


当插入第四个元素的时候, 就超过了扩容阈值,所以触发了扩容,所以看的最后其实是进行了一次扩容,打印出来的capacity是 8.


那么我们应该传多少?


4/0.75  + 1 = 6.3333333


我们指定传6么? 还是传 7 ?


指定6:


image.png


指定7:


image.png


指定6,7 都没区别好像, 值得庆祝的是,没有再次触发扩容。


那么为啥没区别呢?


image.png


HashMap会转换成大于该capacity 的第一个2的幂作为容量 。


所以传5,6,7,8 都是 8 ;


传9,10,11,12,13,14,15,16 都是 16 ;


好了不多啰嗦了, 最后再补一嘴, 默认指定容量,其实就是 内存换性能。


所以真正去使用指定容量的时候, 需要考虑,


如果我是一个定时任务,允许跑1小时。。。 我需要考虑性能么?


或者如果我服务内存很小,我是不是要对内存省吃俭用?

相关文章
|
7月前
|
算法 Java 开发者
为啥HashMap的默认容量是16?
为啥HashMap的默认容量是16?
72 0
|
2月前
|
机器学习/深度学习 C# 索引
HashMap的容量为什么一定是2^n?
`HashMap` 的容量设计为 `2^n` 主要出于三个考虑:1) 位运算效率高,通过 `(hash & (capacity - 1))` 快速计算索引;2) 元素分布均匀,减少哈希冲突,提高性能;3) 扩容时只需检查最高位,简化重分布过程,提升扩容效率。初始容量为 `1 &lt;&lt; 4`(16),扩容以2倍递增。
HashMap的容量为什么一定是2^n?
|
7月前
|
Java
Java为什么建议初始化HashMap的容量大小?
【5月更文挑战第3天】Java中初始化HashMap容量能提升性能。默认容量16,扩容按当前的1/2进行。预估元素数量设定合适容量可避免频繁扩容,减少性能损耗。过大浪费内存,过小频繁扩容,需权衡。Java 8后扩容策略调整,但核心仍是预估初始容量以优化性能。
97 1
|
Java 索引
蚂蚁金服Java研发岗二面:说说HashMap 中的容量与扩容实现
JDK1.8 中 HashMap 的底层实现,我相信大家都能说上来个 一二,底层数据结构 数组 + 链表(或红黑树) ,源码如下
|
7月前
|
Web App开发 存储 数据可视化
VisualVM【实践 01】工具VisualVM下载使用及插件Visual GC示例说明HashMap初始化容量initialCapacity的影响(源码及visualvm_215.zip分享)
VisualVM【实践 01】工具VisualVM下载使用及插件Visual GC示例说明HashMap初始化容量initialCapacity的影响(源码及visualvm_215.zip分享)
118 0
|
存储 缓存 Java
Java中使用HashMap时指定初始化容量性能一定会更好吗?
可以看出,容量16是个分水岭,当容量为16时,二者几乎没啥差异,这也很容易理解,当不指定容量时默认初始容量就是16。但容量大于16时,指定容量时的性能会高于不指定时的性能,随着数量的增加,前者会比后者性能高出50%。但当数据量小于16时,不指定容量大小反而性能更高,最多甚至相差2倍,这就和我们之前的认知不一样了。
89 0
|
Java 索引
81. 说说HashMap 中的容量与扩容实现
81. 说说HashMap 中的容量与扩容实现
80 0
81. 说说HashMap 中的容量与扩容实现
|
消息中间件 Java Kafka
【从Java面试题看源码】-HashMap 初始容量 计算方法
【从Java面试题看源码】-HashMap 初始容量 计算方法
【从Java面试题看源码】-HashMap 初始容量 计算方法
|
消息中间件 Java Kafka
【面试题看源码】-HashMap 初始容量 计算方法
【面试题看源码】-HashMap 初始容量 计算方法
【面试题看源码】-HashMap 初始容量 计算方法
|
存储 算法 索引
HashMap 容量为什么总是为 2 的次幂?
HashMap是根据key的hash值决策key放入到哪个桶(bucket)中,通过 tab=[(n - 1) & hash] 公式计算得出,其中tab是一个哈希表。
HashMap 容量为什么总是为 2 的次幂?