HashMap与ConcurrentHashMap

HashMap

HashMap是数组(桶)+链表+红黑树(JDK1.8后,若链表长度大于8时,则转化为红黑树)

final int hash;    //用来定位数组索引位置
final K key;
V value;
Node<K,V> next;   //链表的下一个node

Node是HashMap的一个内部类,本质是就是一个映射(键值对),HashMap使用哈希表来存储的。HashMap采用了链地址法解决冲突。

int threshold;             // 所能容纳的key-value对极限 
final float loadFactor;    // 负载因子
int modCount;  
int size;

首先,Node[] table的初始化长度length(默认值是16),Load factor为负载因子(默认值是0.75),threshold(阈值)是HashMap所能容纳的最大数据量的Node(键值对)个数。threshold = length * Load factor。key为null的Node在table[0]位置,value也可为null。

下面为hash()方法和indexFor()方法的源码解析,第二步中h ^ (h >>> 16)的原因是,因为table的length一般比较小,只需要考虑到较低的16位即可,提高运算的效率。关于第三步其实是取余运算,因为Node[] table的length长度总为2的幂,h%length等价于 h&(length-1),但是位运算比取余运算快得多。

static final int hash(Object key) {   //jdk1.8 & jdk1.7
     int h;
     // h = key.hashCode() 为第一步 取hashCode值
     // h ^ (h >>> 16)  为第二步 高位参与运算
     return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

static int indexFor(int h, int length) {  //jdk1.7的源码,jdk1.8没有这个方法,但是实现原理一样的
     return h & (length-1);  //第三步 取模运算
}
扩容机制

即 resize() 方法,Java 中,数组的长度是固定的,当 HashMap 中的键值对数量超过阈值时,进行扩容,长度变为原来的两倍,要重新计算键值对的位置,并把它们移动到合适的位置上去。HashMap线程不安全,多线程调用resize时,会出现死循环,链表产生环。下面为产生死循环讲解


图上面为线程1进行resize方法,正处理元素5(e)时,下一个元素指向9(next),线程时间片用完,或其他原因导师线程暂停,线程2直接执行完resize方法。


线程1被唤醒,继续执行,table[1]的第一元素指向5,其next指向元素9。


线程1继续执行,处理完5后,处理9,而此时9的next有指向5,即出现循环链表,这时table[1]的第一个元素指向9,处理9后,继续处理5,5就会为这时table[1]首个Node,此时的next为9,9的next为5,出现死循环。


ConcurrentHashMap

猜你喜欢

转载自blog.csdn.net/programmer_czh/article/details/80473649