什么是HashMap?
HashMap是Java的一个集合容器. 用于存储Key-Value对
HashMap的应用场景
假定如下场景:记录张三,李四,王五三位同学的成绩.
这时候我们有必要去做一个实体类内部包含姓名和成绩两个字段吗?
没有必要的.
完全可以利用HashMap.
存入:
Map<String,double> map=new HashMap();
map.put("张三",60.0);
map.put("李四",80.5);
map.put("王五",79);
取出:
double zhangsanScore=map.get("张三");
double lisiScore=map.get("李四");
double wangwuScore=map.get("王五";
HashMap底层(层层深入)
1.HashMap的底层结构
在1.7的时候为数组加链表的结构,节点为entry
在1.8的时候改为了数组加链表加红黑树的结构,节点为node
为什么1.8加入了红黑树?
为了提高查询效率.
为什么加入红黑树就可以提高查询效率?
因为…这个跟HashMap的关系不是特别大,关于HashMap是提高查询效率,至于是为什么,大家可以自行查阅红黑树相关的资料,后期我也会写关于二叉树,红黑树这些的文章.
为什么1.7叫node而1.8叫entry?
因为引入了红黑树,红黑树的节点叫node.避免冲突.
2.HashMap的数据插入顺序
1.7是头插法
1.8是尾插法
什么是头插法?什么是尾插法?
顾名思义,头插法就是从数据的前面插入,比如
现在数组中的数据为1,2 我要插入3之后,就变成了 3,1,2
尾插法就是从数据的尾部插入,还是上边的例子,现在数组中的数据为1,2 插入3则为1,2,3
为什么要改为尾插法?
因为头插法会导致扩容过程中出现链表循环.
什么是扩容?为什么要扩容?
为什么要扩容: 数组的长度是有限的,而我们对数据的存储需求是无限的,所以我们需要在运行的时候对数组进行扩容来满足我们的业务需求.
至于什么是扩容,往下看.
3.HashMap的扩容机制.
HashMap默认的容量是16.负载因子是0.75.
为什么默认的容量是16?
便于位运算,提高效率. 关于具体的位运算相关.可以查看位运算相关的知识.
负载因子是什么?
负载因子,怎么说呢. 可以这么说 如果 数组当前数据量大于等于容量*负载因子的结果,则进行扩容.
那么负载因子是什么?自己体会一下.
HashMap是如何扩容的?
调用resize() 然后调用transfer() 其中需要rehash()
意思就是
新建一个原数组长度2倍长的数组,然后进行数据转移,将数据重新取Hash值放到新数组上.
为什么扩大到2倍?
同默认容量16.为了便于位运算,提高效率.
为什么1.7扩容过程会出现链表循环?
在多线程情况下,因为是头插法,所以
当第一个线程 指定完指针和next后,线程暂停
然后第二个线程去操作.操作完成后 数据顺序改变
比如 原来是 1 2 3 的顺序
然后第一个线程的指针指向1 next指向2
当第二个线程操作结束后,顺序变为3 2 1
此时,第一个线程刚刚恢复.所以它的next还是2 指针还是1.
这时候就发现,next在指针之前了. 到这里就可以看出链表循环了.
这里如果有机会我会单独写一个图文文章详细解释,
为什么要rehash?
因为扩容改变了数组长度,hash取值是跟数组长度有关联的.
HashMap是线程不安全的
因为HashMap是无锁的,所以线程不是安全的.
那用谁替代HashMap来保证线程安全呢?
HashTable或ConcurrentHashMap或其它…
暂且比较HashTable和ConcurrentHashMap.
HashTable是直接在方法上加锁,效率较慢.
所以选择并发情况下性能较好的ConcurrentHashMap
未完待续…
如果您有更好的指正或补充,欢迎您不吝赐教!