HashMap
是Map
接口最常用的一种实现,内部是基于数组(桶)+链表+红黑树(JDK1.8)实现,内部不允许重复的键,允许null作为键(只能一个),值可以重复,元素排序是通过对与键进行hash(散列法)之后排序。
HashMap是用来存储Key-Value键值对的一种集合,这个键值对也叫做Entry,而每个Entry都是存储在数组当中,因此这个数组就是HashMap的主干。 HashMap数组中的每一个元素的初始值都是NULL。
1.Put方法的实现原理: HaspMap的一种重要的方法是put()方法,当我们调用put()方法时,比如hashMap.put("Java",0),此时要插入一个Key值为“Java”的元素,这时首先需要一个Hash函数来确定这个Entry的插入位置,设为index,即 index = hash("Java"),假设求出的index值为2,那么这个Entry就会插入到数组索引为2的位置。 但是HaspMap的长度有限,默认容量为16,当数组中元素的个数到达加载因子的临界值(默认的加载因子时是 :0.75 (触发扩容临界比例))时会触发扩容 ,当插入的Entry越来越多时,不同的Key值通过哈希函数算出来的index值肯定会有冲突,即元素存储产生hash碰撞时会将原本数组转换为一个链表(单链表) 。 其实HaspMap数组的每一个元素不止是一个Entry对象,也是一个链表的头节点,每一个Entry对象通过Next指针指向下一个Entry对象,这样,当新的Entry的hash值与之前的存在冲突时,只需要插入到对应点链表即可。 需要注意的是,新来的Entry节点采用的是“头插法”(jdk1.8之前),而不是直接插入在链表的尾部,这是因为HashMap的发明者认为,新插入的节点被查找的可能性更大。但是头插法在jdk1.8就被抛弃了,因为头插法在多线程的时候会产生死循环,最后还是采用尾插法解决了这个问题。在链表的长度到达8并且数组长度到达64时,会将链表转换为红黑树(JDK1.8引入),当对元素进行删除时,红黑树节点减少到6时会将树还原成链表结构。
2.Get方法的实现原理 get()方法用来根据Key值来查找对应点Value,当调用get()方法时,比如hashMap.get("apple"),这时同样要对Key值做一次Hash映射,算出其对应的index值,即index = hash("apple")。 前面说到的可能存在Hash冲突,同一个位置可能存在多个Entry,这时就要从对应链表的头节点开始,一个个向下查找,直到找到对应的 Key值,这样就获得到了所要查找的键值对。 例如假设我们要找的Key值是"apple": 第一步,算出Key值“apple”的hash值,假设为2。 第二步,在数组中查找索引为2的位置,此时找到头节点为Entry6,Entry6的Key值是banana,不是我们要找的值。 第三步,查找Entry6的Next节点,这里为Entry1,它的Key值为apple,是我们要查找的值,这样就找到了对应的键值对。
如果有大佬有更好的见解,还望进行斧正,谢谢。