HashMap
⭐对key的hash运算
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
通过对key的异或运算 ^ ,将高位与低位进行互换,从而减低了hash冲突,进而降低了系统损耗。
⭐public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
1.首先判断HashMap是否经过初始化,如果没有经过初始化,默认槽点的数量为16,阀值为负载因子0.75*默认槽点的数量为12.这是判断是否需要扩容的依据。
2.根据(n - 1) & hash算法(ps:其实就是 hash mod size结果为槽点位置),找到槽点的位置,然后判断槽点上是否有值:
2.1如果没有,则直接将Node节点放到槽点上即可。
2.2如果有,则判断if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
解释:(旧的Node的节点.hash与插入Node的节点.hash是否相等 && ((旧的Node的节点.key与插入Node的节点.key的地址是否相等) || (插入Node的节点.key不为NULL && 插入Node的节点.key与旧的Node的节点.key的值是否相等))
)
2.2.1如果找到则退出循环,将旧Node节点的值进行替换;
2.2.2如果找不到,则尾插法,插入到链表的尾部,并且判断链表长度是否>=8,如果大于,则将链表转变成红黑树,便于查找使用(ps:以链表举例,JDK1.8尾插法,JDK1.7头插法)
⭐public V remove(Object key)
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {****}
1.先判断该HashMap槽点链是否为NULL,是否有元素,并找到要删除的槽点判断该槽点上是否有元素
1.1有,则继续执行;
1.1.1判断槽点上的元素是否正是要删除的元素;
1.1.1.1如果是,则 tab[index] = node.next;
1.1.1.2如果不是,则进行从头到尾进行遍历,(ps:以链表为例)
do {
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
找到后,则 p.next = node.next;
1.2没有,则return null;
⭐public V get(Object key)
if ((tab = table) != null && (n = tab.length) > 0 &&(first = tab[(n - 1) & hash]) != null)
1.🎇先判断该HashMap的槽点链不为NULL,并且有元素,并且通过hash计算key找到该槽点,槽点上是有值的
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
1.1❀判断要找的key是否为该槽点的首元素,如果是则返回该Node节点
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
1.2❀如果不是则对该槽点上的链表进行从头到尾遍历(ps:以链表举例,链表数量大于8,则该槽点上的数据结构为红黑树)