开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 12 天,点击查看详情
Map:
编辑
HashMap时使用频率较高的用于映射处理(键值对)的数据类型,底层数据结构是由"数组+链表+红黑树"组成,初始化大小为16,它根据键的hashCode值来存储数据,大多数情况下可以直接定位到它的值,因而具有较高的访问速度,遍历顺序是不确定的;同时允许多个线程同时操作,可能导致数据不一致,线程不安全,可以使用Collections的synchronizedMap()方法使其具有线程安全的能力,或者使用ConcurrentHashMap;
HashMap解释:
它包含一个Node的内部类,该内部类实现了Map.Entry接口,本质上是一个映射(键值对),HashMap使用哈希表来进行存储,系统调用key的hashCode()方法得到一个hashCode值,然后通过hash算法的后两部运算(高位运算和取模运算)来定位该键值对的存储位置,当两个key的hashCode值相同时,表示发生了Hash碰撞,使用更好的Hash算法和扩容机制来解决Hash碰撞,它的初始容量默认为16(所能容纳的最大的Node个数),负载因子默认为0.75,当超过负载因子*初始化长度时会进行扩容,扩容默认为当前容量的两倍(先判断扩容前数组是否需要扩容,需要扩容创建一个新的数组,将原有数组内容转移到新数组中);
关于JDK1.8将hashCode异或其右移十六位:优化了高位运算的算法,通过高十六位或低十六位实现,可以再数组table的length比较小的时候也能保证高低Bit都参与Hash计算中,同时不会有太大的开销;
关于hash值要与length-1相比:把hash值对数组长度进行取模运算消耗大,没有位运算快.当length总是2的n次方时&符号运算等价于取模运算,但是具有更高的效率;
关于数组长度时2的幂次方:便于使用&进行类似取模运算,在h的二进制与操作效率会变快,空间也不会浪费;
关于为什么不直接用红黑树而是用链表转红黑树:当元素小于八个时链表结构能保证查询性能,链表搜索时间复杂度时O(n),当元素大于八个时,红黑树需要进行左旋,右旋,变色操作来保持平衡,搜索时间复杂度是O(logn),红黑树也会提高查询效率,但是新增节点的效率会下降;
关于负载因子为0.75:它只是一个相对于时间效率和空间效率的一个平衡选择,一般不会做出改变;
编辑
ConcurrentHashMap解释:
JDK1.7中:保证线程安全的原因在于使用了锁分离技术,使用多个锁来控制对Hash表的不同部分进行修改,使用多个锁控制对hash表的不同部分进行修改,内部使用段(Segment)来表示这些不同的部分,初始化默认值为16,如果操作发生在多个不同的段上就可以并发进行;
JDK1.8中:摒弃了段的概念,并发控制使用Synchronized和CAS来操作;
HahsSet解释:
底层使用HashMap存储数据,所有操作都是调用HashMap相关方法完成,区别是元素无序,不能重复添加,只能包含一个为null的值;
HashTable解释:
基于Dictionary类,HashMap是基于Map接口的实现;经过Synchronized修饰,线程安全;
LinkedHashMap解释:
集成HashMap,它包含的散列表是双向列表,因此在散列表元素比较多的情况下,查找和删除效率更高;