使用HashMap优化代码,你得学我这样做( 二 )


使用HashMap优化代码,你得学我这样做
本文插图
JDK8以后改成了尾插法 , 原有数据的链表位置不发生改变 , 就不会出现上述情况 。
但是即便是这样 , HashMap也不是线程安全的 , HashMap还是无法保证上一秒put的值 , 下一秒get的时候还是原值 , 因为put和get方法并没有加锁 。
正确使用HashMap
在项目中我们经常用HashMap来缓存数据 , 但是阿里开发手册规范写明 , 创建HashMap时候 , 要指定HashMap的容量 , 最好是2的幂 。
为什么是2的幂?
答案:这样是为了保证位运算方便 , 这样可以减少hash碰撞 , 数据分配均匀 。
利用HashMap缓存数据:
这个是经常使用到的优化 , 这里面要关注的是多线程情况下 , HashMap的线程安全问题 , 在多线程情况下 , 更推荐使用CurrentHashMap
利用HashMap减少for循环:
我们来看一个算法题:
第一种解法:
可以发现 , 这是一种常见的思维 , 用了双重循环 , 遍历了两次 , 时间复杂度是O(n2)O(n^2)O(n2)
那么还要什么更优的解法嘛 , 可以引入HashMap , 记录下每个元素出现的次数 , 解法如下:
上述解法 , 也用了两层for循环 , 但是不是嵌套的 , 所以时间复杂度是O(2n),由于时间复杂度和系数无关 , 所以上述解法的时间复杂度为O(n)O(n)O(n)
在大多数业务场景下去除嵌套循环 , 都可以采用上述方式 , 可以减少时间复杂度 , 但也是有前提条件的 , 前提条件就是外层或者内层循环的遍历次数是已知数据量较小 ,过大的数据量 , 可能会导致内存溢出 , 而且过多的数据 , HashMap也会发生Hash碰撞 , 存储结构会变成数组+链表 , 甚至+红黑树 , 这个时候的HashMap查询复杂度就会变成O(n)或者O(logn) 。
HashMap需要我们进行正确地使用他 , 但是不能滥用 , 我之前在的一家公司 , 就见过有人把10万条数据读取到HashMap上存储 , 每条数据所在内存还比较大 , 然后进行转换 , 到4万条数据的时候 , 就内存溢出了 。
HashMap是存储在内存上的 , 使用的时候 , 一定要尽量避免内存溢出情况 。
HashMap扩容
HashMap的扩容是没有太多变化的 。
其实本文没有去讲JDK8的扩容机制 , 主要去讲了JDK8HashMap的优化点 , 以及如何使用HashMap去优化我们的代码 。
其实扩容机制还有一个问题:为什么HashMap的负载因子是0.75?
答案:假设负载因子为1的情况 , 那么一个默认容量为16的HashMap , 只有当table数据结构16个位置都被占满了 , 才会发生扩容 , 那么出现Hash冲突的情况会增加 , 底层的红黑树变得异常复杂 , 牺牲了时间 , 保住了空间 。 反之过小的负载因子 , 会过早的进行扩容 , Hash冲突减小了 , 可是牺牲了空间 , 0.75是一个中庸的选择 。