Map键值对 Key-Value
- Map集合体系的顶级接口
- 存储的是key-value数据(我们正在研究Map下的集合类的时候,关注点在key上)
- 有的子实现key是有序的,有的子实现key是无序的
- 子实现key是不允许重复的(重复的定义不一样)
- 有的子实现key允许存储null,有的子实现key不允许存储null
API
官方文档
void | clear() 从此映射中移除所有映射关系(可选操作)。 |
boolean | containsKey(Object key) 如果此映射包含指定键的映射关系,则返回 true。 |
boolean | containsValue(Object value) 如果此映射将一个或多个键映射到指定值,则返回 true。 |
Set<Map.Entry<K,V>> | entrySet() 返回此映射中包含的映射关系的 Set 视图。 |
boolean | equals(Object o) 比较指定的对象与此映射是否相等。 |
V | get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null 。 |
int | hashCode() 返回此映射的哈希码值。 |
boolean | isEmpty() 如果此映射未包含键-值映射关系,则返回 true。 |
Set<K> | keySet() 返回此映射中包含的键的 Set 视图。 |
V | put(K key, V value) 将指定的值与此映射中的指定键关联(可选操作)。 |
void | putAll(Map<? extends K,? extends V> m) 从指定映射中将所有映射关系复制到此映射中(可选操作)。 |
V | remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。 |
int | size() 返回此映射中的键-值映射关系数。 |
Collection<V> |
|
解释
以上黄字红底的是在面试的时候必须要掌握的,不然真的凉凉
下面两个图描述的是前后端不分离和分离的两个模型
HashMap 重点 面试必问
结构特点:数组+链表+红黑树
- HashMap是Map接口的一个具体实现
- 哈希表,HashMap底层接口是数组+链表+红黑树(红黑树是jdk1.8版本后新加的结构)
- 数组的默认初始长度16(第一次添加元素产生长度为16) 数组扩容机制,扩容为原来的2倍-->数组长度必定2的幂值-->位运算
- HashMap存储Key是无序的
- HashMap不允许存储重复的key值(对于HashMap来说key的重复定义是啥)
- HashMap允许存储null建
- HashMap线程不安全
- HashMap的加载因子是0.75,可以在构造方法中传入因子,建议在0.5-1之间,不要太荒唐
- HashMap中hash值的计算:Hash = (h = key.hashCode())^(h >>> 16);
- 如果我们在构造方法中给定一个长度,那么hashmap会创建一个底层数组长度大于等于给定值的最小的2的幂值的一个数组 (ArrayDeque是产生大于,而非大于等于)
- 主观上 我们希望每个key都可以散列到数组的不同位置,实际上是没有办法保证的。实际上在我们hash值取下标(和数组长度取模)的过程中,只有hash值的低位才会有用,也就意味着,我们最终在hashmap数组上所散列的位置依赖于hash的低位。
- 我们存储到数组位置的key-value数据,实际上数组存储的是一个Node类型的结点,
- 45
- 也就意味着,如果我们用一个普通的java对象来充当key的时候,我们希望比较的是这个对象里面的存储的内容,那么要根据这个对象里面的参数重写hashCode和equals方法(仅重写equals方法是不行的) 如果两个
- 如果要存储的key-value数据,这个key值已经在HashMap上存在,那么我们会用新的这份key-value数据的value来覆盖已经存储到HashMap上的(已经存储重复的),key-value的value值(key值不会覆盖)
- 在HashMap存储中,如果多个key-value数据散列到同一个下标位置,key值也不重复,那么这个位置就会构建出一个单链表,如果单链表过长,那么这个链表要转化为红黑树。所谓的过长,是指链表长度超过8达到9的时候 这里有个勘误,很多地方都说>=8的时候,就会转化为红黑树。算上新添加的结点 里
- 在HashMap中,某一个数组位置链表长度超过8达到9的时候,一定会转化为红黑树吗
为什么这样?因为考虑到随便就转化红黑树的话,效率会变低。扩容,key-value的hash是不变的,hash来源于key的hasCode,数组长度遍历。取模变化结果->存储位置有可能变化
- 如果一个key-value数据原本在下标为x的位置,当发生扩容的时候,它只可能重新散列表的两个位置:因为数组长度是2的幂值
原本的x位置
X + 就长度的位置到这一步就行了,不想在往底层看了....
- 红黑树什么时候转化为链表?
有两种情况会导致红黑树转化为链表:
one:删除红黑树上的元素,如果链表刚转化为红黑树,那么这时候删除红黑树一个元素,并不会马上变回链表
two:扩容的时候,会导致红黑树有可能转化回链表,扩容会把一个元素位置的数据拆成两个位置 - 存储到红黑树(特殊二叉搜索树)是要能比较大小,在
- 已经存储到HashMap中的key-value不要通过引用来修改key值,
寄语:希望给位看官宝贝儿们看源码不要过度解读,看的是核心逻辑
所以h >>> 16 位的原因是希望高位数也能参加到取模运算中来,以达到充分散列的效果(希望)
加载因子是数组(key-value数据)到一定容量的时候,就会扩容数组,避免链表和红黑树过多过长导致存储效率变低
扩容阈值=加载因子 * 数组长度
HashMap是怎么存储key-value数据的
- HashMap在存储一份key-value数据的时候,先把key取出来,计算
- key经过计算后,得到一个int类型的hash值
- 把这个经过计算的hash值和HashMap的数组长度取模,得到下标
- 如果这个下标位置,没有元素,把这份key-value数据,存到这个下标位置(存储的是一个结点类型,这个结点类型里面包含了key,value)
- 如果这个下标位置,已经存储了结点,判断key值和这个下标位置的这些已经存在的结点中的key值是否重复,如果重复不添加,如果不重复,添加到这个位置(链表的尾部)
- 如果链表过长,把链表转化为红黑树
注意:从单纯数学领域(当下),hash
Hash算法:
MD4
MD5
SHA1
SHA2
构造方法
可以去jdk开发手册上去看
HashMap判断key值重复的依据:根据hash值和equals方法来判断的