简易讨伐HashMap
1 谈谈你对HashMap的认识吧。
HashMap底层由 数组+链表/红黑树 实现。
HashMap首先维护了一个数组,数组中的每个元素是一个Entry对象,每个Entry对象包含四个属性:[key、value、next、hash]。
向HashMap容器中插入Entry对象时,会先使用对象的key调用哈希函数计算出一个hash值,然后使用这个hash值和[数组长度-1] 做位与运算得到一个数组下标。如果数组该下标的位置为空,就插入,如果下标位置上已有其它Entry对象,说明发生了hash冲突(也叫hash碰撞),就把要插入的Entry对象和该位置上的其它Entry对象通过next属性连接起来形成链表。
1.1 HashMap什么时候用链表,什么时候用红黑树?
数组上某个下标位置上的结点数目增加到8个时,链表转换成红黑树,之后,当结点数目降到6个时,红黑树转换成链表。
链表转换成红黑树的阈值为8,是因为在理想情况下,所有结点在数组内遵循泊松分布,在数组的某个下标上链表长度达到8的概率微乎其微,这就保证了绝大部分情况下,链表不会转换成红黑树。
红黑树转换成链表的阈值为6,是为了避免链表和红黑树之间频繁地来回转换。
1.1.1 为什么不直接用红黑树呢?
HashMap是为了提升查询效率才采用红黑树结构,但是,
当链表长度很小的时候,即使不转换成红黑树,查找速度就已经够用了。
链表转换成红黑树会消耗资源。
链表转换成红黑树之后,会占用较大的空间。
所以能用链表满足查询效率需求,就尽量避免转换成红黑树。
1.2 链表的插入方式是头插入还是尾插入?
JDK1.8以后,由原来的头插入改成了尾插入。
如果采用头插入方式,在并发场景下,扩容时可能会出现循环链表的情况,采用尾插入方式会避免这一情况发生。
1.3 红黑树的数据结构是什么样的?
红黑树是 平衡二叉查找树。
二叉查找树的规则是任意结点的左子树(如果有)上的所有结点的值均小于该结点的值,右子树(如果有)上的所有结点的值均大于该结点的值。
红黑树在二叉查找树的基础上做了平衡,保证每个结点的左子树和右子树的高度差最大为2,如果超过了就进行调平衡。
调平衡操作包括左旋、右旋和变色。红黑树的任何不平衡问题都能在三次旋转之内解决。
1.4 遍历HashMap的时间复杂度是多少?
HashMap根据key查找数组下标的时间复杂度为O(1),不影响整体遍历的时间复杂度。
然后遍历链表的时间复杂度是O(n),遍历红黑树的时间复杂度是O(logn)。
1.5 哈希函数是怎么计算hash值的?
向HashMap容器中插入Entry对象时,会先使用对象的key调用一个native方法hashCode(),得到一个int类型的hashCode,然后将(32位的)hashCode右移16位,与hashCode本身做异或运算,得到hash值。
1.5.1 为什么要进行hashCode的高低位异或运算?
为了让hash值更加不确定,降低hash冲突的概率。
1.6 怎么使用hash值计算数组下标?
使用hash值和 [数组长度-1] 做位与运算,得到一个0到 [数组长度-1] 的数,就是插入位置的下标。
HashMap的数组长度一定是2的n次幂,所以 [数组长度-1] 换算成二进制的每一位都是1。反过来,这也就是为什么HashMap的数组长度必须是2的n次幂。
2 你知道HashMap的扩容机制吗?
新建的HashMap容器的容量为16,加载因子默认为0.75。
当 HashMap容器中的元素数量>=容量*加载因子 时,HashMap会进行扩容。每次扩容HashMap的容量会扩大一倍(×2)。
JDK1.7及之前,扩容的思想是:使用一个容量更大的数组来代替原来的数组,将原数组内的元素拷贝到新数组当中。拷贝过程会遍历原数组内的元素,将元素依次插入到新数组当中。
JDK1.8及之后,只需要将原数组内 各元素的hash值与原数组长度 做位与运算,若结果为0,元素位置不变,若结果不为0,元素位置的下标变为 原位置下标+原数组长度。这样经过数组扩容后,元素要么在原位置,要么在 原位置向右移动原数组长度 的位置。
2.1 为什么加载因子默认是0.75?
当 HashMap容器中的元素数量>=容量*加载因子 时,HashMap进行扩容。
可以看出,加载因子越大,HashMap容器中的空间利用率越高,但相应的,hash冲突的概率越高。加载因子越小,空间利用率越低,hash冲突的概率越低。
加载因子默认是0.75是对空间利用率和hash冲突概率的折衷。
3 如何在高并发的情况下使用HashMap?
两种方案。
第一种方案是java.util包提供了包装类Collenctions,里面提供了包装方法synchronizedMap(hashMap)。
第二种方案是java.util.concurrent包提供了HashMap的替代类ConcurrentHashMap类。
3.1 ConcurrentHashMap是怎么保证线程安全的?
JDK1.7及以前:
ConcurrentHashMap使用分段式锁来保证线程安全,可以理解为把一个Map容器拆成n个Segment容器,每个Segment容器分配一把锁,同一时间只允许一个线程持有这把锁。但是宏观上,多个线程可以同时访问这个Map容器。
ConcurrentHashMap是由一个Segment数组和多个HashEntry数组+链表组成的,Segment数组中的每一个元素都是Segment容器,存储一个HashEntry数组+链表。当线程执行添加或删除时,只锁住对应的Segment容器,不影响对其它Segment容器的操作。
JDK1.8及以后:
不再使用Segment+HashEntry+链表的结构了,改为像HashMap一样的数组+链表/红黑树的结构。对链表/红黑树的头/根结点加synchronized锁,在同一时间,只能有一个线程对该链表/红黑树进行操作。
4 如果我要用HashMap存一万条数据,怎么做能提高效率?
预定义存储空间,减少HashMap扩容次数。预定义存储空间 = 数据量/加载因子 + 1。
减小负载因子,虽然降低了空间利用率,但是也减少了hash碰撞的概率。
简易讨伐HashMap相关推荐
- java hash简易_Java手写简易版HashMap的使用(存储+查找)
hashmap的基本结构 package com.liuyuhe; public class node { int hash; object key; object value; node next; ...
- Rocksdb的事务(二):完整事务体系的 详细实现
文章目录 1. 基本事务操作 1.1 TransactionDB -- Pessimistic 1.2 OptimisticTransactionDB 1.3 Read Uncommitted 1.4 ...
- 动手实现一个 LRU cache
前言 LRU 是 LeastRecentlyUsed 的简写,字面意思则是 最近最少使用. 通常用于缓存的淘汰策略实现,由于缓存的内存非常宝贵,所以需要根据某种规则来剔除数据保证内存不被撑满. 如常用 ...
- 06-散列(Hash)基础分析
文章目录 散列(Hash)基础分析 什么是散列表? 如何理解散列设计? 如何解决散列冲突? Java中散列应用分析与实践? 如何对散列(Hash)函数进行设计? 数据插入时线性探测过程是怎样的? 开放 ...
- LRU使用LinkedHashMap实现(主要分析LinkedHashMap的原理)
LC上有这么一道题让实现一个LRU,LRU如上描述就是一个有容量限制当容量满时会自动移除最后一次时间时间最晚的缓存结构. 想到Redis中的ZSET结构(主要是想到了昨天学的漏斗限流-),不过只能存储 ...
- 第8章系统服务(简易音频播放器的实现)
开发一个简易音乐播放器,要求实现: 综合使用Service,BroadCast,ContentProvider等组件实现后台播放. 播放和暂停.上一首.下一首.停止: 后台播放功能, 按下返回键退出应 ...
- 029_自己实现一个HashMap
import java.util.AbstractCollection; import java.util.AbstractSet; import java.util.Collection; impo ...
- 肝一波 ~ 手写一个简易版的Mybatis,带你深入领略它的魅力!
零.准备工作 <dependencies><dependency><groupId>mysql</groupId><artifactId>m ...
- Shiro + JWT + Spring Boot Restful 简易教程
点击上方 好好学java ,选择 星标 公众号重磅资讯,干货,第一时间送达今日推荐:推荐19个github超牛逼项目!个人原创100W +访问量博客:点击前往,查看更多 作者:Smith-Cruis ...
最新文章
- 零售流通ERP系统——基础信息的确立与实施
- Response.Redirect、 Server.Transfer、Server.Execute三者区别
- lcd timing 先关参数
- 按钮 小程序 弹出菜单_公众号怎么关联小程序
- 用计算机连接路由器,用路由器怎么连接两台电脑
- thymeleaf与jsp_PagingAndSortingRepository –如何与Thymeleaf一起使用
- 前端性能优化篇——浏览器同域名并发请求对限制
- 记一次ajax交互问题
- .NetCore Session.Redis
- Linux vi中查找字符内容的方法
- Wince Cab Manager___cab工具
- .Net程序员学用Oracle系列(18):PLSQL Developer 攻略
- 形式化方法(一) 逻辑部分概念梳理
- 4款U盘随身操作系统推荐
- h5跳转app指定页
- ewebeditor 3.8php漏洞,asp eWebEditor v3.8 列目录漏洞
- ValueError: Wrong number of items passed 2, placement implies 1
- cesium入门(八)geojson和topojson
- 防火墙开放21端口linux,linux防火墙开放80,3306,21,443端口
- 蓝桥杯 ADV-222 求arccos