文章目录
1.HashMap的概念
HashMap基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。(除了非同步和允许使用 null 之外,HashMap 类与 Hashtable 大致相同。)HashMap不保证映射的顺序,特别是它不保证该顺序恒久不变。
- HashMap和Hashtable的区别?添加链接描述
2.底层数据结构
HashMap底层采用哈希表结构(数组+链表、JDK1.8后为数组+链表+红黑树)实现,结合了数组和链表的优点:
数组优点: 通过数组下标可以快速实现对数组元素的访问,效率极高;
链表优点: 插入或删除数据不需要移动元素,只需修改节点引用,效率极高。
HashMap图示如下:
HashMap内部使用数组存储数据,数组中的每个元素类型为Node<K,V>:
//Node包含了四个字段:hash、key、value、next,其中next表示链表的下一个节点。
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;
}
public final K getKey() {
return key; }
public final V getValue() {
return value; }
public final String toString() {
return key + "=" + value; }
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
public final boolean equals(Object o) {
if (o == this) return true;
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
if (Objects.equals(key, e.getKey()) &&
Objects.equals(value, e.getValue()))
return true;
}
return false;
}
}
2.JDK1.8之前存在的问题?
JDK1.8之前,HashMap底层实现用的是数组+链表。(JDK1.8以后用数组+链表+红黑树)。
HashMap通过hash方法计算key的哈希码,然后通过(n-1)&hash 公式(n为数组长度,初始化数组默认长度为16),得到key在数组中存放的下标。
当两个key在数组中存在的下标一致时(哈希冲突,哈希碰撞),数据将以链表的方式存储。
在链表中查找数据必须从第一个元素开始一层一层往下找,直到找到为止,时间复杂度为O(N),所以当链表长度越来越长时,HashMap的效率越来越低。
哈希码如何计算?添加链接描述
如何解决?
加入红黑树
当链表中的元素超过8个(TREEIFY_THRESHOLD)并且数组长度大于64(MIN_TREEIFY_CAPACITY)时,会将链表转换为红黑树,转换后数据查询时间复杂度从O(N)变为O(logN)。
红黑树的节点使用TreeNode表示:
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
TreeNode<K,V> parent; // red-black tree links
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev; // needed to unlink next upon deletion
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next)