Map
Map及其实现类
-
Hashtable:
-
在jdk1.0时出现,为在java最早出现的键值对,之所以在jdk1.2之后出现HashMap是因为Hashtable保证了线程的安全效率,但是效率低,而HashMap不保证线程的安全,其效率高于Hashtable
-
在Hashtable中大部分方法都添加了Synchronized的关键词,从而保证了线程的安全性
-
HashMap可以存储null值的key和value
-
而在之后的即使涉及到线程安全的问题时,也不再使用Hashtable,而是使用Collections的工具类使得HashMap变为线程安全的
-
-
HashMap:
-
JDK1.2时出现
-
当下JDK最常用的Map实现类
-
不能保证线程的安全,但是配合Collections的工具方法可以实现线程的安全
-
实现数据结构:
JDK7.0之前:
- 数组 + 链表
JDK8.0:
- 数组 + 链表 + 红黑树
-
-
LinkedHashMap:
-
JDK1.4时出现
-
遍历Map时可以按照添加的顺序进行遍历,而不是随机的Key值进行遍历,原因:在基本的HashMap的基础上,添加了前后指针,使得可以指向添加前后的元素,从而完成有序遍历的操
-
对于频繁的遍历操作,LinkedHashMap的效率高于HashMap
-
-
TreeMap:
- 底层实现与普通的HashMap不同,对于一般有排序特点的数据结构,底层使用的为红黑树
- 按照key进行排序便于有序的遍历
- 通过实现Comparable或定制排序Comparator从而完成排序
-
Properties:
- 常用来处理配置文件
- Key和Value均为String类型
-
CurrentHashMap:
- 实现线程安全
- 实现分段锁
- 保证了高并发
Entry
- 在Map中Put的并非两个独立属性,而是一个Entry的接口匿名实现,Entry的Key实现和Value实现
- key为不可重复的,无序的(Set作为容器【并非HashSet,但是可强转为HashSet】)
- value为可重复的无序的,使用Collection进行存储
- key所在的类需要重写hashCode和equals方法
HashMap底层实现
jdk7.0
-
当进行HashMap map = new HashMap()的初始化后,创建了长度位16的Entry数组
-
对于put(key,value):
-
调用key所在类的hashCode,若hashCode与已插入的值不相同,则添加成功,否则:
-
调用key所在类的equals,若与已存在的的Equals的值不相同则添加成功,否则:
-
刷新所对应的key的value【这也是reSet value的方法】
-
-
在不断添加的过程中,若涉及到扩容问题,默认扩容为原来的2倍:
- 扩容条件,数组长度 >= 12(临界因子 * 16)且插入位置不空(若插入位置为空则直接插入即可,不需要生成链表)
-
在索引的位置存储元素的方式为链表
-
添加新的元素采用头插法,即:
void createEnrty(int hash,K key,V value,int bucketIndex){ Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry(hash,key,value,e); size++; }
static class Entry<K,V>{ final K key; V value; Entry<K,V> next; int hash; Enrty(int h,K k,V v,Entry<K,V> n){ // 头插法添加新的entry元素到entrylist中 value = v; next = n; key = k; hash = h; } }
- 为什么要到threshold就扩容,而不是满了再扩容?
- ans:因为数组为hash码,可能有的位置始终没有元素,而有的位置形成了链表,所以,当阈值因子较小时,链表的长度较短,但是利用率低,当阈值因子较长时,链表较长,查找速率下降,所以阈值因子为0.75
jdk8.0
- 在创建Map时,底层没有创建长度为16的数组
- jdk8.0底层为Node类型的数组,而非entry类型的数组
- 首次调用Put方法时底层创建长度为16的数组
- 当某一个索引的数组位置上的元素以链表形式存在的数据个数 > 8且数组的长度 > 64时,该位置的所有数据升级为红黑树,红黑树为二叉排序树,使得当hashCode相同时查找的效率更快,而不是查找链表
- 在JDK8.0中HashMap的构造方法变为了尾插法,而不是之前的头插法
LinkedHashMap
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after; // 在继承了HashMap的Node的基础上添加了指向前后Entry的指针
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}