1.什么是HashMap???
它是java集合框架的一种实现类,它存储的内容是键值对(key-value)映射.
它继承与AbstractMap抽象类.
它的底层实现原理是,在jdk1.8之前是链表+数组在jdk1.8之后是数组+链表+红黑树,根据哈希函数计算键的哈希值,根据哈希码存储到对应的数组位置上.
它的实现不是底层同步的,意味这不是线程安全.它的key和value可以有一个null.
2.一些HashMap底层的详细信息.
1.源码中重要的常量:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; 默认容量16
static final int MAXIMUM_CAPACITY = 1 << 30; 最大容量2^30
static final float DEFAULT_LOAD_FACTOR = 0.75f; 默认加载因子0.75
static final int TREEIFY_THRESHOLD = 8;链表长度大于默认值8转化为红黑树.
static final int UNTREEIFY_THRESHOLD = 6; 红黑树存储的元素个数小于6,转为链表.
2.HashMap的扩容
当HashMap里面的元素越来越多时,产生哈希冲突的几率就会越来越高,因为数组的长度是默认的,为了降低哈希冲突,就要对集合扩容.
怎么扩容的?
当元素个数超过了数组大小*loadFactor时,就会扩容,loadFactor的默认值为DEFAULT_LOAD_FACTOR = 0.75,也就是说在默认的情况下,数组大小DEFAULT_INITIAL_CAPACITY=16;那么当元素个数超过了16*0.75=12的时候,会调用resize()方法将数组的大小扩容为2*16=32,也就是原来的2倍.然后重新计算每个元素在数组的位置.
3.分析put()方法添加数据的过程
1)判断键值对是否为空,为空计算哈希值,找到到数组索引位置直接添加数据.
!!!不为空判断元素个数是否达到临界值(数组的容量*加载因子)?, --没有达到计算哈希值,插入到数组索引位置接2). --达到了调用resize()方法扩容为当前的2倍,在插入到数组索引位置接2)
2)判断当前位置是否有数据?有 当前的key和存在的key是否相等? 是替换value值.如果不想等会出现哈希冲突,
怎么解决2)出现的哈希冲突呢?
链式寻址法
把存在Hash冲突的key以单向链表的方式来进行存储。在jdk1.8之后通过链式寻址法和红黑树的方式解决Hash冲突问题,其中红黑树就是为了优化链表过长导致时间复杂度增加的问题,当链表长度大于8并且给Hash表的容量大于64的时候,再向链表中添加元素,就会触发链表向红黑树的一个转换。
再hash法
某个Hash函数计算的Key存在冲突的时候,再用另外一个Hash函数对这个Key进行Hash,一直运算直到不再产生冲突为止。
这种方式会增加计算的时间。性能上会有一些影响。