HashMap继承AbstractMap<K,V> ,implements Map<K,V>, Cloneable, Serializable
键值对存储,HashMap最多只允许一条记录的key值为Null(多条会覆盖);允许多条记录的Value为 Null.
源码中可以看到一个静态的内部类(单向链表):static class Node<K,V> implements Map.Entry<K,V>
其实现Map.Entry<K,V>接口
也就是HashMap实际上是一个“链表的数组”的数据结构,每个元素存放链表头结点的数组,即数组和链表的结合体。
HashMap进行put操作的时候 :
1.先判断table(Node<K,V>[]数组)是否为空或者长度为0 如果为空则通过resize()进行扩容长度为16,负载因子为0.75
2.根据key,计算hash得出数组下标i,进行检查,该下表是否为空,如果没有值则直接插入进去
1)如果有值则说明hash冲突,如果table[i]的首个元素 是否和key一样,(通过hashCode和equals判断)相同,则将新的值覆盖旧的值。
2)如果key不相等,则判断是否是红黑树类型,如果是红黑树,则交给红黑树追加此元素。
3)如果key既不相等,也不是红黑树,则是链表,那么就遍历链表中的每一个key和给定的key是否相等。如果,链表的长度大于等于8了,则通过循环将链表改为红黑树,jdk1.8的新特性。
4.如果这三个判断返回的 e 不为null,则说明key重复,则更新key对应的value的值。
5.迭代器的modCount 变量加一。
6.最后判断,如果当前数组的长度已经大于阀值了。则重新hash。
流程图来自:https://blog.csdn.net/visant/article/details/80045154
下面为部分源码分析 主要是put操作分析
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
private static final long serialVersionUID = 362498820763181265L;
//默认初始容量16,必须为2的幂
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;
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
static final int MIN_TREEIFY_CAPACITY = 64;
static class Node<K,V> implements Map.Entry<K,V> {
final int hash; //存储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;
}
.............
//返回此映射项的哈希值: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;
}
}
/*因为没有完美的哈希算法可以彻底避免碰撞,所以只能尽可能减少碰撞,在各方面权衡之后得到一个折
中方案*/
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
//table是一个Node<K,V>[]数组类型,而Node<K,V>实际上就是一个元素值为<key,value>对的单向链
//哈希表的"key-value键值对"都是存储在Node<K,V>数组中的。
transient Node<K,V>[] table;
//用来指向entrySet()返回的set集合
transient Set<Map.Entry<K,V>> entrySet;
//HashMap的大小,即保存的键值对的数量
transient int size;
//用来实现fail-fast机制的,记录HashMap结构化修改的次数
transient int modCount;
//下次需扩容的临界值,size>=threshold就会扩容
//如果table数组没有被分配,则该值为初始容量值16;或若该值为0,也表明该值为初始容量值
int threshold;
//加载因子
final float loadFactor;
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;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
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);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
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;
}
}