一、Map的实现类的结构:
|---Map:为双列数据,存储key-value对的数据;
|---HashMap:作为Map的主要实现类;线程不安全,效率高;key/value可以为null;
|---LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历
原因:在原有的HashMap底层结构基础上,添加了一对指针,分别指向前一个和后一个元素。对于频繁的遍历操作,执行效率要高于HashMap。
|---TreeMap:按照添加的key-value进行排序,实现排序遍历。考虑key的自然排序和定制排序;底层使用红黑树。
|---HashTable:古老的实现类;线程安全,效率低;key/value不能为null;
|---Properties:处理配置文件。key/value为String类型。
Map接口继承树:
2.HashMap底层结构:
jdk7之前使用:数组+链表
jdk8及以后使用:数组+链表+红黑树
二、Map结构的理解:
①Map中的key:无序的、不可重复的,使用Set存储所有key —>key所在的类要重写equals()和hashCode()(以HashMap为例)
② Map中的value:无序的、可重复的,使用Collection存储所有的value —>value所在的类要重写equals()。
③一个键值对:key-value构成了一个Entry对象。
Map中的Entry:无序的、不可重复的,使用Set存储所有的Entry。
三、HashMap的底层实现原理?
1.以jdk7为例说明:
HashMap map = new HashMap();
在实例化以后,底层创建了长度为16的一维数组Entry[] table.
…可能已经执行过多次put…
map.put(key1,value1);
1. 首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算后,得到在Entry数组中的存放位置。
1.1 如果此位置上数据为空,此时key1-value1添加成功(entry添加成功) --->1
1.2 如果此位之上的数据不为空,(意味着此位之上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个/多个数据的哈希值:
1.2.1 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功;--->2
1.2.2 如果key1的哈希值与已经存在的某一个数据(key1-value2)的哈希值相同,继续比较:调用key1所在类的equals(),比较:
1.2.2.1 如果equals()返回false:此时key1-value1添加成功;--->3
1.2.2.2 如果equals()返回true:使用value1替换相同key1的value2值。
补充:关于2和3:此时key1-value1和原来的数据以链表的方式存储。在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,默认的扩容方式:扩容为原来容量的2倍,并将原来的数据复制过来。
2.jdk8 相较于 jdk7在底层实现方面的不同:
1.new HashMap():底层没有创建一个长度为16的数组;
2.jdk8 底层的数组是:Node[], jdk7是Entry[];
3.首次调用put()方法时,底层创建长度为16的数组;
4.jdk7底层结构只有:数组+链表。 jdk8中底层结构:数组+链表+红黑树;
4.1形成链表时,jdk7:新的元素指向旧的元素。jdk8,:旧的元素指向新的元素,(七上八下);
4.2当数组的某个索引位置的元素以链表形式存在的数据个数 > 8且当前数组的长度 > 64,此时此索引位置上的所有数据改为使用红黑树存储;
默认的初始化容量:16
四、Map中定义的方法:
增:put(object key, object value)
删:remove(Object key)
改:put(object key, object value)
查:get(object key)
长度:size()
遍历:keySet() / values() / entrySet()
五、HashMap源码中的一些重要常量:
DEFAULT INITIAL CAPACITY : HashMap默认容量,16;
MAXIMUM_ CAPACITY : HashMap最大支持的容量, 2^30;
DEFAULT LOAD_ FACTOR: HashMap默认加载因子;
TREEIFY THRESHOLD: 当Bucket中链表长度大于此默认值,转化为红黑树存储;
UNTREEIFY THRESHOLD: Bucket中红黑树存储的Node小于该默认值,转化为链表存储;
MIN_ TREEIFYCAPACITY:桶中的Node被树化时最小的hash表容量。( 当桶中Node的
数量大到需要变红黑树时,若hash表容量小于MIN_ TREEIFY CAPACITY时,此时应执行resize扩容操作这个MIN TREEIFY_ CAPACITY的值至少是TREEIFY _THRESHOLD的4倍。)
table:存储元素的数组,2的n次幂;
entrySet:存储具体元素的集;
size: HashMap中存储的键值对的数量;
modCount: HashMap扩 容和结构改变的次数;
loadFactor:填充因子;
threshold:扩容的临界值,= 容量*填充因子。
六、HashMap与LinkedHashMap内部存储源码比较:
1.HashMap内部以Node存储源码:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
}
2.LinkedHashMap继承于HashMap,内部使用Entry存储源码:
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}