HashMap存储过程分析
Posted zhaoyuwei
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HashMap存储过程分析相关的知识,希望对你有一定的参考价值。
1:hashMap的实现原理
1.1 hsahMap在jdk1.8的时候做了一个改进,在jdk1.7的时候hahsMap是基于哈希表(数组+链表)实现的,在1.8之后又加了一个叫二叉树的一个实现,在二叉树里边用了一个叫红黑树,红黑树是二叉树里边的一种,它主要是用来保证树的平衡性,因为二叉树有的时候节点太长,有的时候节点太短,太长的话就不便于遍历,所以说,红黑树的这个目的是来标记(红,黑)的这个算法,主要是保证这棵树的左右两端的平衡,这样遍历的话平均的这个性能就会很好,接下来看一下源码
2:源码分析
2.1首先我们看一下他的默认构造方法
public HashMap() { this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted }
我们把DEFAULT_LOAD_FACTOR点开,我们可以看到,他默认赋了一个0.75的一个值,这个值是加载因子,还有他的初始化容量数组是16,指的是数组的百分之75都被存了值,就表示这个空间的数据已经存满了,如果满的话这个数组就有可能会去重新创建,就是说这个数组要重新扩充了,这个0.75就是标准的红线,他的内部回去计算这个0.75,他是这样计算的,初始化容量数组默认是16,因子是百分之75,当数组到12的时候就已经存满了,当然了它还提供了别的构造方法,在初始化的时候我们也可以去修改这个值
/** * The default initial capacity - MUST be a power of two. 初始化数组值默认为16 */ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 /** * The load factor used when none specified in constructor. 加载因子默认为0.75 */ static final float DEFAULT_LOAD_FACTOR = 0.75f;
2.2:如何把对象存储到哈希表中,他的存储方法叫PUT,PUT方法的时候我们传入一个key,一个value,调用的是一个putvalue的方法,putvalue里边还调用了一个hash(key)的一个方法,是通过key来计算 hash的值
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
他是先拿到Object的一个对象去求(key.hashCode)对象的值,h就拿到了Object key值,h右移16位在和hashCode疑惑最后得出一个整数,这个整数代表的是这个hash 的值
static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); }
得到整个整数以后,他就传到了putval这个方法,我们捡重要的去看一下,首先看一下,下边的一个table,这个table就是Node的这个数组,table其实就是node的一个对象数组,node就是hash里边的一个静态类,里边hash就是我们求的值,next是一个链表,然后把key和value,他把key和value存在了Node对象里边了
transient Node<K,V>[] table;
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
我们可以看到他判断了一下,如果table=null或table=0那么table就等于resize()方法,里边进行了扩容还有一些判断如果他们都等于空,他会把默认的,他会把默认的数组长度16赋值给他,那么n就等于16,我们看一下他存储的位置,(n-1)=15 ,15&hash赋值给i,tab接收作为数组的下标进行存储到Node,Node是一个链表,存储的对象很多,如果又一个对象存储进来了求的值跟他一样就是形成一个链表,jdk1.8之前就是用这个链表处理的,但是1.8之后,出现了红黑树,来限制这种链表,他这个判读是这样写的(binCount>=TREEIFY_THRESHOLD-1)那么(THREEIFY_THRESHOLD)就等于7,下一个数据就是等于8,当大于8的时候就转换成了红黑树
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; }
3.存储过程
3.1 刚才进行源码分析,我想大家也了解到了他的一个存储过程,在这里我在总结一下:
把key通过通过hash()方法计算hash的值,然后这个hash的值对数组进行求余数(默认16),来决定KEY对象在数组中的位置,当这个对象有多个值时,以链表方式进行存储,jdk8以后当链表长度大于8时,链表将转换成红黑树结构存储,这样的目的,是为了取值更快,存储的数量越多性能的表现就越明显。
以上是关于HashMap存储过程分析的主要内容,如果未能解决你的问题,请参考以下文章