在做 HashSet源码分析 时,发现其底层用的是HashMap,所以今天来研究下 HashMap的源码。
1.变量
/**
* 默认初始容量16,必须是2的幂
*/
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;
/**
* 最大容量,2的30次幂
*/
static final int MAXIMUM_CAPACITY = 1 << 30;
/**
* 默认负载因子
*/
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;
/**
* 节点数组
*/
transient Node<K,V>[] table;
/**
* 缓存的 entrySet()
*/
transient Set<Map.Entry<K,V>> entrySet;
/**
* 大小:map中包含的键值对的数量
*/
transient int size;
/**
* 修改次数
*/
transient int modCount;
/**
* 扩容后的目标大小: (capacity * load factor)
*/
int threshold;
/**
* 负载因子
*/
final float loadFactor;
2.构造方法
/**
* 创建一个空的hashmap
* 使用默认的初始容量*(16)和默认的加载因子(0.75)
*/
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
/**
* 创建一个空的hashmap
* 使用指定的初始容量 initialCapacity 和默认的加载因子(0.75)
*/
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
/**
* 创建一个空的hashmap
* 使用指定的初始容量 initialCapacity 和指定的加载因子loadFactor
*/
public HashMap(int initialCapacity, float loadFactor) {
// 校验初始容量
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
// 校验负载因子
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
// 根据 initialCapacity 返回合适的目标容量(2的次幂)
this.threshold = tableSizeFor(initialCapacity);
}
/**
* 创建一个能容纳m的HashMap,将m中元素放入,使用默认负载因子0.75
*/
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
/**
* 返回一个大于输入参数且最近的2的整数次幂的数
*/
static final int tableSizeFor(int cap) {
int n = cap - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}
注:如果两个不同对象的hashCode相同,这种现象称为hash冲突。
我们来看一下hash节点(hashmap里的基础单元)的结构:
static class Node<K,V> implements Map.Entry<K,V> {
//key和value的hashcode异或值
final int hash;
//键
final K key;
//值
V value;
//下一个节点:说明hasnmap是数组+单向链表结构
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;
}
}
put相关方法:
/**
* 将value与该map中的指定key相关联。
* 如果map已包含指定key,返回oldValue,否则返回null
*/
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
/**
* 将value与该map中的指定key相关联。
* 如果map已包含指定key,则不替换并返回oldValue,否则替换并返回oldValue
*/
@Override
public V putIfAbsent(K key, V value) {
return putVal(hash(key), key, value, true, true);
}
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
/**
* 实现 Map.put 方法.
*
* @param hash key的哈希值
* @param key
* @param value
* @param onlyIfAbsent 如果为true, 不更改现有值
* @param evict 如果为false, 表处于创建模式
* @return 返回之前的value,没有则返回null
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
HashMap.Node<K, V>[] tab;
HashMap.Node<K, V> p;
int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
//初始化哈希表
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
//通过哈希值找到对应的位置,如果该位置还没有元素存在,直接插入
tab[i] = newNode(hash, key, value, null);
else {
HashMap.Node<K, V> e;
K k;
//如果该位置的元素的 key 与之相等,则直接到后面重新赋值
if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof HashMap.TreeNode)
//如果当前节点为树节点,则将元素插入红黑树中
e = ((HashMap.TreeNode<K, V>) p).putTreeVal(this, tab, hash, key, value);
else {
//否则一步步遍历链表
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
//插入元素到链尾(1.8之前是头插法,并发情况下插入引发扩容时容易造成死循环)
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1)
//元素个数大于等于 8,改造为红黑树
treeifyBin(tab, hash);
break;
}
//如果该位置的元素的 key 与之相等,则重新赋值
if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
//前面当哈希表中存在当前key时对e进行了赋值,这里统一对该key重新赋值更新
if (e != null) {
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
//检查是否超出 threshold 限制,是则进行扩容
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
/**
* 把 m 里所有值放入当前map
*/
public void putAll(Map<? extends K, ? extends V> m) {
putMapEntries(m, true);
}
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
if (table == null) {
//节点数组为null,需确认初始容量
float ft = ((float)s / loadFactor) + 1.0F;
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
if (t > threshold)
threshold = tableSizeFor(t);
}else if (s > threshold){
//s大于目标容量,需扩容
resize();
}
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
//节点数组赋值
putVal(hash(key), key, value, false, evict);
}
}
}
get相关方法:
/**
* 返回指定key的value,没有则返回null
*/
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
/**
* 返回指定key的value,没有则返回defaultValue
*/
@Override
public V getOrDefault(Object key, V defaultValue) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
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);
}
}
return null;
}
remove 和 clear 相关方法:
/**
* 删除指定key的元素,返回value,没有则返回null
*/
public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
@Override
public boolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true, true) != null;
}
/**
* 实现 Map.remove 相关方法
*
* @param hash key的hash值
* @param key
* @param value the value to match if matchValue, else ignored
* @param matchValue 如果为true,在value相等时删除
* @param movable 如果为false,删除时不要移动其他节点
* @return the node, or null if none
*/
final Node<K,V> removeNode(int hash, Object key, Object value,
boolean matchValue, boolean movable) {
Node<K,V>[] tab; Node<K,V> p; int n, index;
if ((tab = table) != null && (n = tab.length) > 0 &&
(p = tab[index = (n - 1) & hash]) != null) {
Node<K,V> node = null, e; K k; V v;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
node = p;
else if ((e = p.next) != null) {
if (p instanceof TreeNode)
node = ((TreeNode<K,V>)p).getTreeNode(hash, key);
else {
do {
if (e.hash == hash &&
((k = e.key) == key ||
(key != null && key.equals(k)))) {
node = e;
break;
}
p = e;
} while ((e = e.next) != null);
}
}
if (node != null && (!matchValue || (v = node.value) == value ||
(value != null && value.equals(v)))) {
if (node instanceof TreeNode)
((TreeNode<K,V>)node).removeTreeNode(this, tab, movable);
else if (node == p)
tab[index] = node.next;
else
p.next = node.next;
++modCount;
--size;
afterNodeRemoval(node);
return node;
}
}
return null;
}
/**
* 删除map中所有元素
*/
public void clear() {
Node<K,V>[] tab;
modCount++;
if ((tab = table) != null && size > 0) {
size = 0;
for (int i = 0; i < tab.length; ++i)
tab[i] = null;
}
}
判断相关方法:
/**
* 判断是否包含key的映射
*/
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
/**
* 判断是否包含指定value
* 使用双重循环
*/
public boolean containsValue(Object value) {
Node<K,V>[] tab; V v;
if ((tab = table) != null && size > 0) {
//1.外层循环,取数组的每一个值
for (int i = 0; i < tab.length; ++i) {
//2.内层循环,取链表的每一个值
for (Node<K,V> e = tab[i]; e != null; e = e.next) {
if ((v = e.value) == value ||
(value != null && value.equals(v)))
return true;
}
}
}
return false;
}
public boolean isEmpty() {
return size == 0;
}
replace相关方法
/**
* 替换key的值为value
* retrun oldvalue
*/
@Override
public V replace(K key, V value) {
Node<K,V> e;
if ((e = getNode(hash(key), key)) != null) {
V oldValue = e.value;
e.value = value;
afterNodeAccess(e);
return oldValue;
}
return null;
}
/**
* 如果key的当前值为oldValue,则替换key的值为newValue
* retrun 替换是否成功
*/
@Override
public boolean replace(K key, V oldValue, V newValue) {
Node<K,V> e; V v;
if ((e = getNode(hash(key), key)) != null &&
((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
e.value = newValue;
afterNodeAccess(e);
return true;
}
return false;
}
/**
* 使用biFunction,对map里所有value 用指定的函数执行结果 替换
*/
@Override
public void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
Node<K,V>[] tab;
if (function == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next) {
//value等于function执行结果
e.value = function.apply(e.key, e.value);
}
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
Java1.8对 hashMap 进行了一些改造
附上两篇相关博文,第二篇讲的非常棒
Java HashMap 新增方法
Java 程序员都该懂的 Java8 HashMap