Map定义
Map is called key-value collection, each map element contains key and value.
Map格式
Map Format: { key1=value1, key2=value2, k3=value3, ... }
key is unique. Repeat assign value for the same key, the following value will override the previous value.
map.put("name", "bill");
map.put("name", "jack");
key named name will have value as jack.
HashMap vs HashTable
HashMap | HashTable | |
同步 | no | yes |
安全 | no | yes (使用 synchorized修饰) |
效率 | 高 O(1) | 低 |
允许空键 | yes | no |
数据结构 | 数组+链表 | 数组+链表 (<jdk1.8) 数组+链表+红黑树(>= jdk1.8) |
重复 | no | yes |
初始化 | 16 | 11 |
增长 | new = 2 * old (扩容1倍) | new = 2 * old + 1 (2倍 加 1) |
执行效率
JDK 1.5 推出CurrentHashMap。CurrentHashMap将HashMap分片(默认为16片),对每一片加锁(即分段锁),通过key.hashCode判断对哪一片加锁。
效率:HashMap > CurrentHashMap (分段锁) > HashTable (整个加锁)
HashMap数据结构
- < JDK 1.8,Table数组 + Entry链表
- ≥ JDK 1.8,Table数组 + Entry链表/红黑树
a. 数组+链表:输入元素时首先根据key的hashcode计算hash值,根据hash值得到元素在数组中的位置。如果该位置有值,则该位置以链表的形式存放,新加入的元素放在链尾。如果该位置为空,就当新元素放在此位置。
b. 红黑树:链表中节点超过8个时,链表会转为红黑树来提高查询效率。复杂度从O(n) 变为O(logn)。
public class HashMapDemo {
public static void main(String[] args) {
HashMapDemo map = new HashMapDemo();
map.put("刘一", "刘一");
map.put("陈二", "陈二");
map.put("张三", "张三");
map.put("李四", "李四");
map.put("王五", "王五");
map.put("悟空", "悟空");
}
public static void put (String key, String value) {
System.out.println(key + ":" + key.hashCode() + ":" + Math.abs(key.hashCode()%15));
}
}
/** 运行结果://刘一、张三、悟空的hasdCode相同。因此,key值不同,hashCode可以相同。
* 刘一:671464:4
* 陈二:1212740:5
* 张三:774889:4
* 李四:842061:6
* 王五:937065:0
* 悟空:798139:4
*/
CurrentHashMap
CurrentHashMap默认分片为16片,即16个segment。每个Segment包含多个HashEntry列表数组。
一个Key需要经过3次hash映射,才能确定最终位置。
第一次Hash映射:h1=hash1(key)
第二次Hash映射:h2=hash2(h1高几位),确定元素存放在哪个segment。
第三次Hash映射:h3=hash3(h1) 通过h3确定元素放在哪个HashEntry。
面试题
阐述put过程
第一步:寻找索引位置。计算方法:index = hashcode%数组长度。刘一/张三/悟空在hashmap中的索引值为4。
第二步:将节点依次插入单向链表。
为何使用数组+链表/红黑树
数组大小固定,对key值散列后得到下标。
下标相同即发生冲突(不同key值,索引相同),采用链表对节点链式存放。
链表与红黑树的相互转换,方便于扩容/收缩。
单链表转为红黑树:单链表长度≥8
红黑树转为单链表:红黑树节点≤6
何为红黑树
红黑树是二叉树(树有序、查询效率高、O(logn)),单向链表查复杂度为O(n),左右高度差≤1
HashMap如何扩容
实际长度 > lengh * loadFactor (默认:16 * 0.75)时,HashMap会扩容,长度为原来2倍。
基于时间/空间因素,默认负载因子为0.75。
Map转为JavaBean
hutool BeanUtil