HashMap
源码学习
关系图
属性
代码语言:javascript复制//默认初始容量
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
//最大容量
static final int MAXIMUM_CAPACITY = 1 << 30;
//默认负载因子,太大时间复杂度高,容易碰撞。太小浪费空间,时间复杂度低
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/*相同数据量下红黑树(TreeNode)占用的空间是链表(Node)的俩倍 , 考虑到时间和空间的权衡 , 只有当链表的长度达到阈值时才会将其转成红黑树。
*/
//链表长度最大长度
static final int TREEIFY_THRESHOLD = 8;
//红黑树最小个数,小于6则转成链表
static final int UNTREEIFY_THRESHOLD = 6;
//数组容量
static final int MIN_TREEIFY_CAPACITY = 64;
transient Node<K,V>[] table;
//包含HashMap中所有键值对的set集合
transient Set<Map.Entry<K,V>> entrySet;
transient int size;
//modCount来记录修改集合修改次数
transient int modCount;
//扩容大小
int threshold;
//负载因子
final float loadFactor;
//内部类,实现了Map.Entry,单向链表
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; }
//重写了toString()方法
public final String toString() { return key "=" value; }
//将key的hash值和value的hash值进行异或运算
public final int hashCode() {
return Objects.hashCode(key) ^ Objects.hashCode(value);
}
//赋值,将旧的值返回
public final V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
//key和value的值都一样才返回true
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;
}
}
构造方法
代码语言:javascript复制//默认无参构造方法
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // 将默认加载因子赋值
}
//指定初始化大小
public HashMap(int initialCapacity) {
this(initialCapacity, DEFAULT_LOAD_FACTOR);
}
public HashMap(int initialCapacity, float loadFactor) {
//初始值为负数时,抛出异常
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: "
initialCapacity);
//如果初始容量超过2的30次方,则容量置为2的30次方
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
//如果负载因子如果是非正数或者值为Nan的时候抛出异常
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: "
loadFactor);
this.loadFactor = loadFactor;
//计算需要扩容的大小
this.threshold = tableSizeFor(initialCapacity);
}
计算扩容大小
代码语言:javascript复制//解释了为什么扩容的大小都是2的n次方
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;
}
获取hash值hash(Object key)
代码语言:javascript复制/**
*如果是null,那hash值为0
*为减少hash碰撞,否则将hash值右移16做异或运算
**/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
put操作
代码语言:javascript复制final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
// n代表数组长度 i 代表key在数组下标位置
Node<K,V>[] tab; Node<K,V> p; int n, i;
//判断现在的table是不是空
if ((tab = table) == null || (n = tab.length) == 0)//懒加载 使用的时候在加载
//是的话就进行扩容
n = (tab = resize()).length;
//如果对应下标index师傅冲突 没冲突就直接赋值
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
//如果hash值且equlas内容之也相等, 则直接覆盖
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//如果是红黑树结结构,则直接追加在红黑树上
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);
//如果数组长度>=64且链表长度大于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;
}
}
//将新的value覆盖到老value上
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
modCount;
if ( size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
final Node<K,V>[] resize() {
//赋值
Node<K,V>[] oldTab = table;
//第一次put时肯定table为空 所以得到old=0
int oldCap = (oldTab == null) ? 0 : oldTab.length;
//threshold 下一次扩容的大小
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
else if (oldThr > 0) // initial capacity was placed in threshold
newCap = oldThr;
else { // zero initial threshold signifies using defaults
newCap = DEFAULT_INITIAL_CAPACITY;
//计算下次扩容时的大小
newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
}
if (newThr == 0) {
float ft = (float)newCap * loadFactor;
newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
(int)ft : Integer.MAX_VALUE);
}
threshold = newThr;
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
table = newTab;
//例如当放入第13个key-v时,执行,重新计算index值
if (oldTab != null) {
for (int j = 0; j < oldCap; j) {
Node<K,V> e;
if ((e = oldTab[j]) != null) {
oldTab[j] = null;
if (e.next == null)
newTab[e.hash & (newCap - 1)] = e;
else if (e instanceof TreeNode)
((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
else { // preserve order
Node<K,V> loHead = null, loTail = null;
Node<K,V> hiHead = null, hiTail = null;
Node<K,V> next;
do {
next = e.next;
if ((e.hash & oldCap) == 0) {
if (loTail == null)
loHead = e;
else
loTail.next = e;
loTail = e;
}
else {
if (hiTail == null)
hiHead = e;
else
hiTail.next = e;
hiTail = e;
}
} while ((e = next) != null);
if (loTail != null) {
loTail.next = null;
newTab[j] = loHead;
}
if (hiTail != null) {
hiTail.next = null;
newTab[j oldCap] = hiHead;
}
}
}
}
}
return newTab;
}
判断map是否是空map
代码语言:javascript复制//判断的是长度而不是判断是非为null
public boolean isEmpty() {
return size == 0;
}
根据key获取value
代码语言:javascript复制//获取key的hash值,再根据hash值和key去获取Node
V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
//将当前map的数组赋值给tab,判断它!=null
//判断map数组的长度
//根据hash值取出对应的node
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
/*判断当前的key、value是否和first节点的一样,此first不是数组下标0的first,而是每个下标衍生出来的链表*/
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
//判断是否有next节点
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;
}
判断是否包含某key
代码语言:javascript复制//底层还是调用getNode方法
public boolean containsKey(Object key) {
return getNode(hash(key), key) != null;
}
删除某key
代码语言:javascript复制public V remove(Object key) {
Node<K,V> e;
return (e = removeNode(hash(key), key, null, false, true)) == null ?
null : e.value;
}
/**
* @param hash key的hash值,该值是通过hash(key)获取到的
* @param key 要删除的键值对的key
* @param value 要删除的键值对的value,该值是否作为删除的条件取决于matchValue是否为true
* @param matchValue 如果为true,则当key对应的键值对的值equals(value)为true时才删除;否则不关心value的值
* @param movable 删除后是否移动节点,如果为false,则不移动
* @return 返回被删除的节点对象,如果没有删除任何节点则返回null
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;
//同getNode的判断,这个tab[index = (n-1)&hash]好理解多了
//找node的过程和getNode相同
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;
//操作modeCount记录修改次数
modCount;
--size;
/* 调用afterNodeRemoval方法,该方法HashMap没有任何实现逻辑,目的是为了让子类根据需要自行覆写*/
afterNodeRemoval(node);
return node;
}
}
return null;
}
//与上面的区别是给定了value并指定matchValue为true
@Override
public boolean remove(Object key, Object value) {
return removeNode(hash(key), key, value, true, true) != null;
}
清空map
代码语言:javascript复制//size置为0,数组每个下标都存放null,并不是将数组置为null
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;
}
}
是否包含某值
代码语言:javascript复制public boolean containsValue(Object value) {
Node<K,V>[] tab; V v;
//判断数组是否null或者长度是否为0
if ((tab = table) != null && size > 0) {
for (int i = 0; i < tab.length; i) {
//遍历数组每个下标中的链表
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;
}
keySet()
代码语言:javascript复制//其实是个Set集合,将map中的key值都存放在set集合上
public Set<K> keySet() {
Set<K> ks = keySet;
if (ks == null) {
ks = new KeySet();
keySet = ks;
}
return ks;
}
final class KeySet extends AbstractSet<K> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<K> iterator() { return new KeyIterator(); }
public final boolean contains(Object o) { return containsKey(o); }
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == 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)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
values()
代码语言:javascript复制//类似上面的keySet(),将map中的value值都存放在集合上
public Collection<V> values() {
Collection<V> vs = values;
if (vs == null) {
vs = new Values();
values = vs;
}
return vs;
}
final class Values extends AbstractCollection<V> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<V> iterator() { return new ValueIterator(); }
public final boolean contains(Object o) { return containsValue(o); }
public final Spliterator<V> spliterator() {
return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super V> action) {
Node<K,V>[] tab;
if (action == 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)
action.accept(e.value);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
entrySet()
代码语言:javascript复制//返回包含HashMap中所有键值对的set集合
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
public final int size() { return size; }
public final void clear() { HashMap.this.clear(); }
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
//删除最后还是调用removeNode()方法
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
public final Spliterator<Map.Entry<K,V>> spliterator() {
return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
Node<K,V>[] tab;
if (action == 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)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
getOrDefault(Object key, V defaultValue)
代码语言:javascript复制//获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。
@Override
public V getOrDefault(Object key, V defaultValue) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? defaultValue : e.value;
}
替换值replace方法
代码语言:javascript复制//替换 hashMap 中是指定的 key 对应的 value,返回值是boolean类型
@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;
}
//与上述的返回值不通,替换对应key的value并返回之前的value
@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;
}
foreach
方法
代码语言:javascript复制@Override
public void forEach(BiConsumer<? super K, ? super V> action) {
Node<K,V>[] tab;
if (action == 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)
action.accept(e.key, e.value);
}
//判断modeCount是否改变,如改变抛出异常
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
序列化被transient修饰的变量
代码语言:javascript复制private void writeObject(java.io.ObjectOutputStream s)
throws IOException {
int buckets = capacity();
// Write out the threshold, loadfactor, and any hidden stuff
s.defaultWriteObject();
s.writeInt(buckets);
s.writeInt(size);
internalWriteEntries(s);
}
/**
* Reconstitute the {@code HashMap} instance from a stream (i.e.,
* deserialize it).
*/
private void readObject(java.io.ObjectInputStream s)
throws IOException, ClassNotFoundException {
// Read in the threshold (ignored), loadfactor, and any hidden stuff
s.defaultReadObject();
reinitialize();
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new InvalidObjectException("Illegal load factor: "
loadFactor);
s.readInt(); // Read and ignore number of buckets
int mappings = s.readInt(); // Read number of mappings (size)
if (mappings < 0)
throw new InvalidObjectException("Illegal mappings count: "
mappings);
else if (mappings > 0) { // (if zero, use defaults)
// Size the table using given load factor only if within
// range of 0.25...4.0
float lf = Math.min(Math.max(0.25f, loadFactor), 4.0f);
float fc = (float)mappings / lf 1.0f;
int cap = ((fc < DEFAULT_INITIAL_CAPACITY) ?
DEFAULT_INITIAL_CAPACITY :
(fc >= MAXIMUM_CAPACITY) ?
MAXIMUM_CAPACITY :
tableSizeFor((int)fc));
float ft = (float)cap * lf;
threshold = ((cap < MAXIMUM_CAPACITY && ft < MAXIMUM_CAPACITY) ?
(int)ft : Integer.MAX_VALUE);
// Check Map.Entry[].class since it's the nearest public type to
// what we're actually creating.
SharedSecrets.getJavaOISAccess().checkArray(s, Map.Entry[].class, cap);
@SuppressWarnings({"rawtypes","unchecked"})
Node<K,V>[] tab = (Node<K,V>[])new Node[cap];
table = tab;
// Read the keys and values, and put the mappings in the HashMap
for (int i = 0; i < mappings; i ) {
@SuppressWarnings("unchecked")
K key = (K) s.readObject();
@SuppressWarnings("unchecked")
V value = (V) s.readObject();
putVal(hash(key), key, value, false, false);
}
}
}
HashMap
的key
为什么重写hashcode
和equals
代码语言:javascript复制Object类的hashCode方法:底层采用C语言编写的,根据对象地址转换成整数类型
如果俩个对象的hashcode值相等的情况下,对象的内容值不一定相等。
如果俩个对象的equals值相等,则hashcode值一定相等。
HashMap
如何比较俩个key是否一样的?就是通过hashcode
方法和equals方法比较的
hashmap
如何存放key为null的值
代码语言:javascript复制//通过代码可知,key为null的值存放在数组下标为0的位置,如果下标0已经有key了,那么就采用链表的方式下放null
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}