先上一张UML图,可以清晰的捋一捋几个常用Map的关系
特性:
-
Map主要存储Key Value 键值对的数据结构
HashMap
-
根据键的hashCode值存储数据,具有很快的访问速度
-
HashMap最多只允许一条记录的键为null,允许多条记录的值为null。
-
HashMap非线程安全
LinkedHashMap
-
继承HaseMap
-
记录了插入数据,迭代的时候的先入先出
HashTable
-
遗留类,很多特性和HashTable一致
-
继承自Dictionary类,并且是线程安全(并发性不如ConcurrentHashMap,ConcurrentHashMap引入的是分段锁)
-
不建议使用
TreeMap
-
实现SortedMap接口
-
key需要实现Comparable接口,否则抛java.lang.ClassCastException异常
-
JDK1.8 引入红黑树的数据结构和扩容的优化
-
Map底层的数据是 数组+列表+红黑树
-
HashMap非常重要的一个属性就是Node[] table
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; //索引位置
final K key; //map key Map存储数据时,key存在即覆盖
V value; //map 存储的值
Node<K,V> next; //列表的下一个node
//Node[] 是hashMap的内部类
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
......
}
HashMap的pul方法源码分析
/**
* Implements Map.put and related methods.
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//1.判断table为空就创建
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//2.拆分if左边条件得出 tab[i=(table.length-1)&hash] 结果在[0 ~ 「table.length-1」]
//tba[x「0~(n-1)」] 取出Node p == null 直接添加Node到Map中
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else { //tba[x「0~(n-1)」] 取出Node p != null
Node<K,V> e; K k;
//3.如果 p 不为空,且 p 的 key 与要 put() 进来的 key 一致,则把 e 的值替换成要 put() 进来的值。
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//4.判断该链是否为红黑树
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else { //5.该链为链表
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
//链表长度大于8转换为红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//key已经存在,直接覆盖
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
//超过最大容量就扩容 resize()原理是新建一个大容量数组,而非修改当前引用对象的长度
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
HashMap内存样图
image.pngStructure结构图呢可以看出来,HashMap实现了Map类的put方法,并将相关操作封装到私有的putVal()方法里面了
好好分析一下put方法,因为这个方法涉及列表和红黑树以及扩容,值得重点关注
image-20200610144140126
以上算是好好的研究了一下HashMap的特性,jdk1.8才引入红黑树等优化特性,以及hash碰撞也优化了算法,数据分布均匀呢对效率也有所提升,如果对这里Map结构还有深挖的兴趣呢可以继续挖下去,比如扩容和分段锁,红黑树这些点也还是蛮不错.
网友评论