本篇是HashMap笔记第二篇,在上一篇中分析了HashMap的底层结构,让我们对HashMap有了一个整体印象,在这一篇文章中,我将重点分析HashMap如何通过索引存储及获取数据。
一. HashMap的索引计算方法
HashMap通过put(key,value)来执行存储数据操作,通过get(key)来获取数据,而在这过程中存储的索引是非常重要的,它相当于一个坐标,有了它,就能保证我们在使用hashMap操作时的准确性。
假如调用hashMap.put(“money”,1000)方法,将会在HashMap的table数组中插入一个key是"money"的元素;这时需要通过hash()函数来确定该entry的具体插入位置,而hash()方法内部会调用hashCode()函数得到"money"的hashCode;然后putVal()方法经过一定计算得到最终的插入位置index,最终将这个entry插入到table的index位置。
key的hash值计算是通过hashCode的高16位异或低16位实现的:
h = (key.hashCode()) ^ (h>>16)
使用位运算代替了取模运算,在table的长度比较小的情况下,也能保证hashCode的高位参与到地址映射的计算当中,同时不会有太大的开销。
图一.hashCode计算得到索引过程
HashMap put方法详解
图二.put添加方法执行流程
通过上面的流程的流程图,我门就可以非常清晰的看到HashMap方法的执行流程:
1.判断数组table是否为null,若为null则执行resize()操作。
2.根据键key的值计算hash值得到插入的数组索引i,若table[i] == null;则直接新建节点插入,进入步骤6;若table[i]非null,则继续执行下一步。
3.判断table[i]的首个元素的key是否和当前key相同(hashCode和equals均相同),若相同则直接覆盖value,进入步骤6,反之则执行下一步。
4.判断table[i]是否是treeNode,若是红黑树,则直接在树中插入键值对并进入步骤6,反之执行下一步。
5.遍历table[i],判断链表长度是否大于8,若>8,则把链表转换成红黑树,在红黑树中执行插入操作;若<8,则进行链表的插入操作;遍历过程中若发现key已存在则会直接覆盖该key的value值。
6.插入成功后,判断实际存在的键值对数量size是否超过了最大容量threshold,若超过则进行扩容。
HashMap get方法详解
图二.get方法执行流程
上图是HashMap get方法执行流程图,按照图中的流程我们可以知道get方法的执行步骤:
1.首先用传来的key,通过计算下标的方法计算出相应的数组下标,并获取对应节点n。
2.判断n是否为null,若为null,则返回null并结束;反之进行下一步。
3.判断n的key和要查找的key是否相同(key相同指的是hashCode和equals都相同),若相同则返回n并结束;反之,进行下一步。
4.判断是否有后续节点m,若没有则技术;反之进行下一步。
5.判断m是否是红黑树,若为红黑树则遍历红黑树,在遍历过程中如果存在某一节点的key与要找的key相同,则返回该节点;反之,返回bull;若非红黑树则进行下一步。
6.遍历列表,若存在某一节点的key与要找的key相同,则返回该节点;反之,返回null;
好了,这篇笔记的内容就写到这。下期笔记的内容我将为大家介绍HashMap的扩容机制。