今天看HashMap的源码,主要想看下HashTable和HashMap在放置entry时数据结构的处理方式;
但是我直接打开的是jdk1.8.172版本的源码,怎么看HashMap 的源码都和原来看过记忆中的不太一样,比原来的的方式更加优化,一看版本才发现缘由;
区别:
之前hashmap处理冲突单纯使用链表法进行链接,1.8中,当一散列值中对应的链表长度超过8个后,会将链表转化为红黑树进行存储。
不多说,直接上代码:
1.8之前:
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for the key, the old
* value is replaced.
*/
public V put(K key, V value) {
if (table == EMPTY_TABLE) {
inflateTable(threshold);
}
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
//这里的循环是关键
//当新增的key所对应的索引i,对应table[i]中已经有值时,进入循环体
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
//判断是否存在本次插入的key,如果存在用本次的value替换之前oldValue,相当于update操作
//并返回之前的oldValue
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
//如果本次新增key之前不存在于HashMap中,modCount加1,说明结构改变了
modCount++;
addEntry(hash, key, value, i);
return null;
}
1.8源码:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<k,v>[] tab; Node<k,v> p; int n, i;
//如果tab为空或长度为0,则分配内存resize()
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
//(n - 1) & hash找到put位置,如果为空,则直接put
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<k,v> e; K k;
//第一节节点hash值同,且key值与插入key相同
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) {
//p第一次指向表头,以后依次后移
if ((e = p.next) == null) {
//e为空,表示已到表尾也没有找到key值相同节点,则新建节点
p.next = newNode(hash, key, value, null);
//新增节点后如果节点个数到达阈值,则将链表转换为红黑树
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
//容许null==null
if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;//更新p指向下一个节点
}
}
//更新hash值和key值均相同的节点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;
}
区别很明显,在数据检索的性能上又提高了不少;