目前正在研究HashMap 的源码,写点东西做记录,欢迎大家相互学习,也欢迎大佬进行指点。
- HashMap 静态变量
- HashMap 节点
- HashMap put 操作
HashMap 静态变量
/**
* Map 默认的大小 16。
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
/**
* Map 的最大存储量。
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* Map 因子,CAPACITY * LOAD_FACTOR 用来决定何时需要扩容。
* 为啥是0.75,有待考究,如果有大佬能解惑,希望不吝指教。
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* 确定转换为红黑树的阈值
*/
static final int TREEIFY_THRESHOLD = 8;
/**
* 确定红黑树回退的阈值
*/
static final int UNTREEIFY_THRESHOLD = 6;
/**
* 可以转换成红黑树的数组最小值
*/
static final int MIN_TREEIFY_CAPACITY = 64;
HashMap 节点
Node 节点
HashMap 底层对于数据的存储实际上是以Node 节点(红黑树为TreeNode)的方式进行存储。我们进行put 操作的key-value 则是存入Node 节点中,可以说HashMap 底层的最小单位就是节点。
/**
* Map 内部自己的hash 值计算方法
*/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
/**
* Map 实际的存储对象
*/
static class Node<K,V> implements Map.Entry<K,V> {
/**
* hash 方法计算出的值
*/
final int hash;
/**
* Map 存入的key
*/
final K key;
/**
* Map 存入的value
*/
V value;
/**
* 链表结构,hash 冲突且key 不同时存入
*/
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 V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
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;
}
}
TreeNode 节点
当链表存储的过大,且满足转换成红黑树的条件时,链表会被转换成红黑树。此时节点会重新生成为TreeNode ,当然看源码我们可以知道,TreeNode 实际上还是继承的Node 节点。
/**
* Map 红黑树节点
*/
static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
/**
* 父节点
*/
TreeNode<K,V> parent;
/**
* 左节点
*/
TreeNode<K,V> left;
/**
* 右节点
*/
TreeNode<K,V> right;
/**
* 需要进一步研究红黑树算法
*/
TreeNode<K,V> prev;
boolean red;
TreeNode(int hash, K key, V val, Node<K,V> next) {
super(hash, key, val, next);
}
/**
* 返回红黑树的根节点
*/
final TreeNode<K,V> root() {
for (TreeNode<K,V> r = this, p;;) {
if ((p = r.parent) == null)
return r;
r = p;
}
}
// 省略
...
}
HashMap put 操作
put 操作主要分为两种情况,hash 冲突和不冲突。
- hash 冲突
hash 冲突,即(n - 1) & hash定位到的数组坐标,已经有值存在。此时需要对key 值再进行判断,如果key 相同,则覆盖旧值;key 不同,则需要将节点放入链表尾部,或者若是红黑树节点,则放入红黑树中。
- hash 不冲突
hash 不冲突,则只需直接将节点放入数组中即可。
/**
* Map 存放数据
*
* @param hash map 自有算法((h = key.hashCode()) ^ (h >>> 16),
* key.hashCode()为key 类的hashCode 实现,如String 为s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1],n 为字符串长度,s[i] 为i 位置字符)
* @param key 要存入map 的key
* @param value 要存入map 的value
* @param onlyIfAbsent 是否覆盖
* @param evict
* @return 如果覆盖,则为覆盖前的值
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent, boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
//判断数组是否为空,如果为空则初始化数组
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//判断当前位置是否有值
//没值,则放入新Node(hash,key,value,Node next) 节点
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
//hash冲突,当前位置有值
Node<K,V> e; K k;
//判断这俩个key 是否一样
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
//一样,则把当前节点p 赋值给临时节点e
e = p;
//key 不一样,新节点需要放入链表,如果链表已转成红黑树,则放入红黑树中
//判断节点是否为红黑树节点
else if (p instanceof TreeNode)
//红黑树节点,将新节点放入红黑树中
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
//链表
for (int binCount = 0; ; ++binCount) {
//定位到链表尾端
if ((e = p.next) == null) {
//将新节点插入链表尾端
p.next = newNode(hash, key, value, null);
//判断是否需要将链表转换成红黑树结构
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;
//遍历链表,呼应上文e = p.next
p = e;
}
}
//判断是否已存在
if (e != null) { // existing mapping for key
V oldValue = e.value;
//判断是否需要覆盖
if (!onlyIfAbsent || oldValue == null)
//覆盖值
e.value = value;
afterNodeAccess(e);
//返回旧值
return oldValue;
}
}
//map 修改次数
++modCount;
//判断Map 是否需要扩容
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
未完待续。。。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。