在Java的世界里无论类还是各种数据其结构的处理是整个程序的逻辑以及性能的关键由于本人接触了一个有关性能与逻辑同时并存的问题于是就开始研究这方面的问题找遍了大大小小的论坛也把《Java 虚拟机规范》《apressllections()bmocrshareconnector》和《Thinking in Java》翻了也找不到很好的答案于是一气之下把JDK的src 解压出来研究扩然开朗遂写此文跟大家分享感受和顺便验证我理解还有没有漏洞 这里就拿HashMap来研究吧
HashMap可谓JDK的一大实用工具把各个Object映射起来实现了键--值对应的快速存取但实际里面做了些什么呢?
在这之前先介绍一下负载因子和容量的属性大家都知道其实一个 HashMap 的实际容量就 因子*容量其默认值是 ×= 这个很重要对效率很一定影响!当存入HashMap的对象超过这个容量时HashMap 就会重新构造存取表这就是一个大问题我后面慢慢介绍反正如果你已经知道你大概要存放多少个对象最好设为该实际容量的能接受的数字
两个关键的方法put和get
先有这样一个概念HashMap是声明了 MapCloneable Serializable 接口和继承了 AbstractMap 类里面的 Iterator 其实主要都是其内部类HashIterator 和其他几个 iterator 类实现当然还有一个很重要的继承了MapEntry 的 Entry 内部类由于大家都有源代码大家有兴趣可以看看这部分我主要想说明的是 Entry 内部类它包含了hashvaluekey 和next 这四个属性很重要put的源码如下
public Object put(Object key Object value) { Object k = maskNull(key);
这个就是判断键值是否为空并不很深奥其实如果为空它会返回一个static Object 作为键值这就是为什么HashMap允许空键值的原因
int hash = hash(k); int i = indexFor(hash tablelength);
这连续的两步就是 HashMap 最牛的地方!研究完我都汗颜了其中 hash 就是通过 key 这个Object的 hashcode 进行 hash然后通过 indexFor 获得在Object table的索引值
table?不要惊讶其实HashMap也神不到哪里去它就是用 table 来放的最牛的就是用 hash 能正确的返回索引其中的hash算法我跟JDK的作者 Doug 联系过他建议我看看《The art of programing vol》可恨的是我之前就一直在找我都找不到他这样一提我就更加急了可惜口袋空空啊!
不知道大家有没有留意 put 其实是一个有返回的方法它会把相同键值的 put 覆盖掉并返回旧的值!如下方法彻底说明了 HashMap 的结构其实就是一个表加上在相应位置的Entry的链表
for (Entry e = table[i]; e != null; e = enext) { if (ehash == hash && eq(k ekey)) { Object oldvalue = evalue; evalue = value; //把新的值赋予给对应键值 erecordAccess(this); //空方法留待实现 return oldvalue; //返回相同键值的对应的旧的值 } } modCount++; //结构性更改的次数 addEntry(hash k value i); //添加新元素关键所在! return null; //没有相同的键值返回 }
我们把关键的方法拿出来分析
void addEntry(int hash Object key Object value int bucketIndex) { table[bucketIndex] = new Entry(hash key value table[bucketIndex]);
因为 hash 的算法有可能令不同的键值有相同的hash码并有相同的table索引
如
key=
和key=Object g的hash都是-
那它经过indexfor之后的索引一定都为i
这样在new的时候这个Entry的next就会指向这个原本的table[i]
再有下一个也如此
形成一个链表
和put的循环对定e
next获得旧的值
到这里
HashMap的结构
大家也十分明白了吧?
if (size++ >= threshold) //这个threshold就是能实际容纳的量 resize( * tablelength); //超出这个容量就会将Object table重构
所谓的重构也不神就是建一个两倍大的table(我在别的论坛上看到有人说是两倍加把我骗了)然后再一个个indexfor进去!注意!!这就是效率!!如果你能让你的HashMap不需要重构那么多次效率会大大提高!
说到这里也差不多了get比put简单得多大家了解putget也差不了多少了对于collections我是认为它是适合广泛的当不完全适合特有的如果大家的程序需要特殊的用途自己写吧其实很简单(作者是这样跟我说的他还建议我用LinkedHashMap我看了源码以后发现LinkHashMap其实就是继承HashMap的然后override相应的方法有兴趣的同人自己looklook)建个 Object table写相应的算法就ok啦
举个例子吧像 Vectorlist 啊什么的其实都很简单最多就多了的同步的声明其实如果要实现像Vector那种插入删除不多的可以用一个Object table来实现按索引存取添加等
如果插入删除比较多的可以建两个Object table然后每个元素用含有next结构的一个table存如果要插入到i但是i已经有元素用next连起来然后size++并在另一个table记录其位置