当前位置: 首页 > news >正文

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

前言

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

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


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

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

本篇内容:

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

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

源码走读扩容的点;

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

一些杂谈。

正文

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

 

为什么要指定容量?

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


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

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

 

错误理解使用示例 ① :

分页查询出来的数据,需要转换成 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博客)

 这样就是违背了我们开始指定 的 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());

    }

看下打印效果: 

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


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

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

那么我们应该传多少?

4/0.75  + 1 = 6.3333333

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

指定6:

指定7:

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

那么为啥没区别呢?

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

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

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

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

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

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

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

相关文章:

  • 用这个免费CDN,治愈WordPress网站加载缓慢的大难题
  • 搞什么啊? URI 和 URL 到现在还不清楚?
  • Springboot 根据数据库表自动生成实体类和Mapper,只需三步
  • SpringBoot 导出多个Excel文件,压缩成.zip格式下载
  • Springboot 指定自定义模板导出Excel文件
  • Mysql 我随手造200W条数据,给你们讲讲分页优化。
  • 【云原生】风云暗涌的时代,DBA们的利刃出鞘了
  • 以后面试官问你 为啥不建议使用Select *,请你大声回答他!
  • Springboot 导入导出Excel ,一对多关系,复合表格、合并单元格数据
  • 怎么清晰地理解、表达 IaaS 、 PaaS 、 SaaS ?
  • UML类图的六大关系,最佳学习理解方式
  • Springboot 整合 Socket 实战案例 ,实现 单点发送、广播群发,1对1,1对多
  • Springboot Mybatis 、JPA 调用存储过程,实战教程
  • 写代码的七八九十宗罪,多图、胆小慎入!
  • Springboot byte[] 转 MultipartFile ,InputStream 转 MultipartFile
  • 【Amaple教程】5. 插件
  • 【React系列】如何构建React应用程序
  • 【RocksDB】TransactionDB源码分析
  • k个最大的数及变种小结
  • nodejs调试方法
  • Redis的resp协议
  • SQLServer之创建数据库快照
  • Vue全家桶实现一个Web App
  • 基于游标的分页接口实现
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 前端
  • 浅谈Golang中select的用法
  • 让你成为前端,后端或全栈开发程序员的进阶指南,一门学到老的技术
  • 世界编程语言排行榜2008年06月(ActionScript 挺进20强)
  • 消息队列系列二(IOT中消息队列的应用)
  • 一个项目push到多个远程Git仓库
  • C# - 为值类型重定义相等性
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • ​ssh免密码登录设置及问题总结
  • $.ajax中的eval及dataType
  • (02)Hive SQL编译成MapReduce任务的过程
  • (C语言)球球大作战
  • (Redis使用系列) Springboot 实现Redis 同数据源动态切换db 八
  • (大众金融)SQL server面试题(1)-总销售量最少的3个型号的车及其总销售量
  • (附源码)springboot宠物管理系统 毕业设计 121654
  • (十六)一篇文章学会Java的常用API
  • (一)C语言之入门:使用Visual Studio Community 2022运行hello world
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (转)chrome浏览器收藏夹(书签)的导出与导入
  • (轉貼) 資訊相關科系畢業的學生,未來會是什麼樣子?(Misc)
  • (轉貼)《OOD启思录》:61条面向对象设计的经验原则 (OO)
  • .net core 客户端缓存、服务器端响应缓存、服务器内存缓存
  • .NET Core使用NPOI导出复杂,美观的Excel详解
  • .Net Memory Profiler的使用举例
  • .NET 材料检测系统崩溃分析
  • .NET/C# 使窗口永不激活(No Activate 永不获得焦点)
  • .NET/C# 项目如何优雅地设置条件编译符号?
  • .NET中 MVC 工厂模式浅析
  • /var/lib/dpkg/lock 锁定问题
  • :=