HashMap源码分析
来源:互联网 发布:网络p2p的四条红线 编辑:程序博客网 时间:2024/05/16 07:56
HashMap作为最常用的容器类之一不一定每个人都对它非常了解。即使面试题也仅仅会比较几种容器的性能和线程安全问题。
HashMap如此常用确实有着它精巧的设计,即使我现在也无法真正理解其hash()策略是如何想到的。这里就把我一些理解的写出来,一方面是自己的笔记,另一方面是可以和广大程序员作一个交流,说不定能解开我一些无法解答的迷。
这里先提几个问题作为引子。
1. HashMap是以什么为基础结构来作到快速定位。
2. HashMap是如何解决Key冲突的。
3. 如何保证数组的容量在控制范围内(就是数组的下标是自己能掌控的)
4. HashMap的散列算法。
5. HashMap的一些弊端。
这几个问题如果扩展开来讲都代表着一些思想在里面。好,先从第一个开始。其实我们都知道Map有着Key-Value键值对,这样就可以保证快速定位,这些思想在硬件内存索引,搜索引擎,数据库中都有着类似的具体应用。这里就不展开来讲。下面就是要知道在java的基础类中有哪些可以满足这样的要求,可以直接根据key找到value。可能一时想不到数组,或许很多人没等想出答案已经在不同途径知道答案了。数组(PS:在Java中数组并不是一个常用的结构,一般都会用List来代替)。数组正有这样的特性。其下标正是我们想要的key。
这里又会出两个新问题:a)key是Integer类型;b)如何保证key的唯一性。这就是我上面所提出的第二个问题。为了使key成为Integer类型,可以用一定的算法让各种类型映射成Integer类型,其实这里难就难在如何有一个算法让不同类型的值能映射成Integer的时候唯一,至少保证大部分唯一。这里联想到hashCode可以满足这个要求,而且我们也可以实现自己的hashCode方法来达到这个目的。这样第二个问题就解决了。
这里就会到第三个问题,因为hashCode虽然可以实现唯一,但是下一个问题就出现了,那数组下标如何保证,我们不可能申请一个无限大的内存,而且hashCode是有负数的。这样就引出了HashMap第四个问题,它精巧的hash算法。关于HashMap中的hash()方法是根据位移来保证高位参与^的运算。这是因为当数组申请的容量很小的时候(HashMap默认是16)。很容易造成key冲突。
这样讲估计很难讲清楚,我先把代码贴出来:
第一个是它的hash算法,这里通过位移的方式让高位参与运算,很多人会问为什么要这样做,这里就是为了解决key冲突的算法。
- static int hash(int h) {
- // This function ensures that hashCodes that differ only by
- // constant multiples at each bit position have a bounded
- // number of collisions (approximately 8 at default load factor).
- h ^= (h >>> 20) ^ (h >>> 12);
- return h ^ (h >>> 7) ^ (h >>> 4);
- }
第二个看一下它另外一个精巧的设计,就是如何保证数组key是保证在可控的范围内。这个方法在我第一次看到的时候真心感觉这个设计太精巧了。
- /**
- * Returns index for hash code h.
- */
- static int indexFor(int h, int length) {
- return h & (length-1);
- }
这段代码是put()中的一段,就是用来判断是否有相同值,如果hashCode和key都相同,说明确实相同。极端情况下要把余下的数组扫描完。所以说一个好的哈希算法的重要性。
- for (Entry<K,V> e = table[i]; e != null; e = e.next) {
- Object k;
- if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
- V oldValue = e.value;
- e.value = value;
- e.recordAccess(this);
- return oldValue;
- }
- }
这两个方法其实要合并起来看,第一个方法是保证第二个方法不至于过多的key冲突。这里举个例子,如果HashMap的容量是16,那么(length-1)就是15,二进制是1111,这样它可以存储key从0~15的值,比如我要存储下标为1这个值,这样不通过hash算法将会出现大量的key冲突,比如说二进制0001,10001,110001这样只要低四位是0001的hashCode都可以满足这样要求。hash算法通过高位右移来参与低位的运算以求保证低位的不同。至于具体为什么可以达到这样的目标我也说不上来,看了半天也无法说出个理来。即使这样也不能保证百分之百的不冲突,只是概率会降低很多。
好,第三个问题就到此为止。
第四个问题。关于HashMap的一些弊端。第一个就是对于数组利用率,由于hashCode接近于随机性,导致对于数组的利用率并不能到达100%。大概只有70%左右。第二个就是当删除数据后数组无法弹性伸缩。
HashMap还有其他方法,有兴趣的可以慢慢看。
- 源码分析:HashMap
- 源码分析:HashMap
- HashMap源码分析
- HashMap 源码分析
- HashMap源码分析
- HashMap LinkedHashMap源码分析
- HashMap源码分析
- HashMap 源码分析
- HashMap源码分析
- HashMap源码分析
- HashMap源码分析
- Java HashMap 源码分析
- HashMap源码分析
- java HashMap源码分析
- 源码分析HashMap
- HashMap源码分析
- HashMap源码分析
- HashMap源码分析
- 再谈HashMap-由一个实际问题引发的对HashMap设计吐嘈
- java 初级对象问题
- 11.1
- zTree实现地市县三级级联Action类
- javascript深入理解js闭包
- HashMap源码分析
- UML 使用
- 串(C++实现)
- UML-活动图(Activity Diagram)
- Java性能优化学习之 巧用线程池ThreadPool
- 再记作品展
- 职员有薪水了
- C++类和接口的设计原则探讨
- 普仪金融 阳江