什么是HashMap
HashMap是一个用来存储键值对的数据结构,每一个键值对也叫做Entry,这些键值对(Entry)分散存储在一个数组当中,HashMap的每个元素初始值都为null。
对于HashMap我们有两种常用的方法Put和Get, 声明方式: HashMap map=new HashMap()。
HashMap的默认长度是16并且每次自动扩展或者手动扩展时长度必须为2
的幂。具体请看下面关于HashCode的介绍。**
Put方法
对于Put方法,比如调用map.put(“apple”,0),插入一个key为”apple”的value为0的元素,这个时候需要一个index来确定它在HashMap中的索引位置,但是HashMap的长度是有限的,当插入的Entry越来越多时,会发生index冲突的情况,这个时候我们该怎么办呢?其实在HashMap中每一个元素不止是一个Entry对象,也是一个链表的头结点,每一个Entry对象都会有一个next指针指向它的下一个Entry对象,当新来的Entry发生映射冲突时,只需要插入相应的链表即可。Get方法
我们使用Get方法来根据输入的Key来查找Value会发生什么呢?
首先我们会将输入的Key来做一次Hash映射来得到相应的index,由于刚才所说的Hash冲突,一个index可能对应多个Entry对象,这个时候就要顺着相应的Entry头结点一个一个向下来找。和HashTable的区别
①HashTable是一个线程安全的Map实现,但是HashMap是不安全的实现,所以HashMap的性能要高于HashTable,但是如果在高并发的情况下,HashTable实现类会更好一点。
②HashTable不允许用null值来作为key,value。如果试图把null值来作为元素放入HashTable中会引发NullPointerException异常;但是null可以在HashMap中被支持。关于HashCode
上面提到根据key的值来计算Entry在HashMap中索引位置,这个过程称为一次Hash映射,index=Hash(“element”),那么如何实现一个尽量分布均匀的Hash函数呢,我们利用Key的HashCode来进行某种运算,为了实现高效的Hash算法,HashMap的发明者采用了位运算的方式。
index=HashCode(Key)&(Length-1) ,举个例子,假定book的HashCode结果为十进制的3029737,对应的二进制为1011100011101011101001,再假定HashMap的长度Length为默认的16,Length-1的值为15,对应的的二进制为1111,1011100011101011101001,1111&1011100011101011101001=1001,十进制是9,所以index=9。可以说Hash算法得到的index完全取决于Key的HashCode的后几位。
为什么长度必须为2的幂呢,假定HashMap的长度为10,10-1得到的Length长度为9,对应的二进制为1001,1001&1011100011101011101001,计算出来的结果为1001,虽然还是9,但是我们换个HashCode试试,对于1011100011101011101111来说,运算结果为1001,index的值还是9,当HashMap长度不为2的幂的时候,有的index出现的几率更大,而有的index则永远不会出现比如(0111),这显然违背了Hash算法均匀分布的原则,反观长度为2的幂的时候,最后的HashCode的二进制位全为1,index等同于HashCode后几位的值,只要输入的HashCode本身分布均匀,那么Hash算法的结果就是均匀的。