1,HashMap的底层在jdk1.7的时候是数组,也就是我们说的hash桶和链表,而在1.8的时候,是数组, 链表|红黑树。就是说要么是链表,要么是红黑树。
2, HashMap类实现了Map接口,而在Map接口中,定义了内部接口类,Entry
interface Entry<K,V>
而这个entry 类关注的其实就是K和V,也就是key和value。
3, HashMap的实现,实在类中加了静态内部类Node,Node则是在实现了Entry接口的前提下,增加了hash值,next值:
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
这里的hash,是通过hash算法对key值进行计算得出的值;
这里的key的value,则是当前操作的key和vlaue。
这里的next,是指向链表下一个元素的的内存地址。
3, 解释下next值:
因为链表这个数据结构,分为单向链表和双向链表,无论那个,它在内存中存储不是线性的,就是不是连续的,所以单向的链表,但前向需要指向下一项的地址,那么这个值就是用next这个属性来存储;而双向链表,它不仅要指明下一项的内存地址,还要知道它上一项的内存地址,这个内存地址也是存储到next中,hashmap中用的单项链表,所以用next值存储下一个node的值(下一个node同样是一个K-V键值对)。
4, hashmap中的一些基础信息:
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;
这里逐一解释:
- hashmap如果初始化不定义大小,他的默认大小是16,源码中用移位运算 1 << 4
- hashmap最大的容量的2的30次幂,也就是 1<< 30
- 这里的1是二进制,就是 0001
- 为啥用一位运算,而不是直接给值,是因为以为运算更加接近底层,他的运行效率很高。
- 为初始化是16,这个因为内存地址是用16进制表示,而作为更加机器二进制语言的汇编语言是用16禁止表示的,所以用16。
- 扩容因子是0.75,就是说该扩容的时候并不是当前容量满了,而是在当前容量 乘以 0.75 的时候开始扩容
- 剩下的 8 , 6 , 64 分别是一些临界值,这些数字发生在hash碰撞的时候,多出来的k-v值的存储
具体在第6项中解释。
5, hash怎么存储:
通过每个key值,用hash算法,都会计算出一个对应的值,这个hash值会重复吗?就看这个hash算法强不强了。当key的hash值计算出后,那么就知道把这个node点放到哪里存储了,公式是hash & (hash.size() - 1),意思就是说用key的hash值和当前hash的容量减一之后做【与】运算,就是把hash和size - 1 变为2进制,这样得到的值就是这个node节点存放的位置;那么经过【与】之后,值会重复吗? 当然会,即使hash算法再强,那么经过 【与运算】的值也会重复,一样的 结果怎么存储,这就是hash碰撞。而无论是链表还是红黑树都是解决解决hash碰撞的。、6, 链表和红黑树的关系
当该位置没有数据,那么数据就进来了,且next 值为null,当发生碰撞,新的node节点要往后存储,这里有限选择链表,上一个next值就是该node节点的内存地址,如果同一位置碰撞太多,那么链表的size() 就越大,我们知道由于不是线性存储和他的数据结构,它插入删除修改是极快的,但是设计到查找就会很慢。所以,当前位置挂了 6 或者 少于 6 个,用链表; 当第 8 个时候,用要变换为红黑树;超过64个,用别的结构存储。
7, hash扩容
如果数据太多,到了扩容因子的限制,就要扩容,扩容之后的默认大小:
newThr = oldThr << 1; // double threshold
就是左移 一位,也就是乘 2.
8, 扩容之后的数据分布
第一种: 把key再次hash一次,再次 和容量 做与运算,得出不同的值存储。(rehash在1.7中)
第二种,左移之后,查看当前值的最高位,如果是1的话,那么重新分配, 是0 的话,位置不变。
10, 知识普及:
因为是2进制表示16,所以4个位数为一组,比如:0000 0000, 那前4个一组,后4个一组;
如果现在存储位置 0000 0100, 它的最高为是0, 扩容之后,为0000 1000,它的高位是1
如果是0000 10000 ,高位为1,扩容之后 0001 0000,高位是1
不知大家明白不
欢迎大家指正!