??xml version="1.0" encoding="utf-8" standalone="yes"?>蜜桃精品视频在线观看,中文字幕中文字幕中文字幕亚洲无线 ,中文字幕一区二区三区四区五区http://www.aygfsteel.com/ArcticOcean/category/34757.htmlJust do itQ?/description>zh-cnWed, 11 Aug 2010 11:02:34 GMTWed, 11 Aug 2010 11:02:34 GMT60HashMap原理、源码、实?/title><link>http://www.aygfsteel.com/ArcticOcean/archive/2010/08/09/328316.html</link><dc:creator>Cool Jazz</dc:creator><author>Cool Jazz</author><pubDate>Mon, 09 Aug 2010 07:14:00 GMT</pubDate><guid>http://www.aygfsteel.com/ArcticOcean/archive/2010/08/09/328316.html</guid><wfw:comment>http://www.aygfsteel.com/ArcticOcean/comments/328316.html</wfw:comment><comments>http://www.aygfsteel.com/ArcticOcean/archive/2010/08/09/328316.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.aygfsteel.com/ArcticOcean/comments/commentRss/328316.html</wfw:commentRss><trackback:ping>http://www.aygfsteel.com/ArcticOcean/services/trackbacks/328316.html</trackback:ping><description><![CDATA[HashMap是一U十分常用的数据l构Q作Z个应用开发h员,对其原理、实现的加深理解有助于更高效地进行数据存取。本文所用的jdk版本?.5?<br /> <br /> <span style="font-size: large"><strong>使用HashMap</strong></span> <br /> <br /> 《Effective JAVA》中认ؓQ?9%的情况下Q当你覆盖了equalsҎ(gu)后,请务必覆盖hashCodeҎ(gu)。默认情况下Q这两者会采用Object?#8220;原生”实现方式Q即Q?<br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools">Java代码Q?a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" ></a></div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">protected</span><span> </span><span id="wmqeeuq" class="keyword">native</span><span> </span><span id="wmqeeuq" class="keyword">int</span><span> hashCode();   </span></span> <li><span id="wmqeeuq" class="keyword">public</span><span> </span><span id="wmqeeuq" class="keyword">boolean</span><span> equals(Object obj) {   </span></span> <li><span>    </span><span id="wmqeeuq" class="keyword">return</span><span> (</span><span id="wmqeeuq" class="keyword">this</span><span> == obj);   </span></span> <li><span>}  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">protected native int hashCode(); public boolean equals(Object obj) { return (this == obj); } </pre> <br /> <br /> hashCodeҎ(gu)的定义用Cnative关键字,表示它是由C或C++采用较ؓ底层的方式来实现的,你可以认为它q回了该对象的内存地址Q而缺省equals则认为,只有当两者引用同一个对象时Q才认ؓ它们是相{的。如果你只是覆盖了equals()而没有重新定义hashCode()Q在dHashMap的时候,除非你用一个与你保存时引用完全相同的对象作为key|否则你将得不到该key所对应的倹{?<br /> <br /> 另一斚wQ你应该量避免使用“可变”的类作ؓHashMap的键。如果你一个对象作为键值ƈ保存在HashMap中,之后又改变了其状态,那么HashMap׃产生混ؕQ你所保存的值可能丢失(管遍历集合可能可以扑ֈQ。可参?em><a target="_blank">http://www.ibm.com/developerworks/cn/java/j-jtp02183/</a></em> <br /> <br /> <span style="font-size: large"><strong>HashMap存取机制</strong></span> <br /> <br /> Hashmap实际上是一个数l和链表的结合体Q利用数l来模拟一个个ӞcM于Bucket SortQ以快速存取不同hashCode的keyQ对于相同hashCode的不同keyQ再调用其equalsҎ(gu)从List中提取出和key所相对应的value?<br /> <br /> JAVA中h(hun)ashMap的初始化主要是ؓinitialCapacity和loadFactorq两个属性赋倹{前者表ChashMap中用来区分不同hash值的keyI间长度Q后者是指定了当hashMap中的元素过多少的时候,开始自动扩容,。默认情况下initialCapacity?6QloadFactor?.75Q它表示一开始hashMap可以存放16个不同的hashCodeQ当填充到第12个的时候,hashMap会自动将其keyI间的长度扩容到32Q以此类推;q点可以从源码中看出来: <br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="bar">Java代码Q?/div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">void</span><span> addEntry(</span><span id="wmqeeuq" class="keyword">int</span><span> hash, K key, V value, </span><span id="wmqeeuq" class="keyword">int</span><span> bucketIndex) {   </span></span> <li><span>    Entry<K,V> e = table[bucketIndex];   </span> <li><span>        table[bucketIndex] = </span><span id="wmqeeuq" class="keyword">new</span><span> Entry<K,V>(hash, key, value, e);   </span></span> <li><span>        </span><span id="wmqeeuq" class="keyword">if</span><span> (size++ >= threshold)   </span></span> <li><span>            resize(</span><span id="wmqeeuq" class="number">2</span><span> * table.length);   </span></span> <li><span>}  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">void addEntry(int hash, K key, V value, int bucketIndex) { Entry<K,V> e = table[bucketIndex]; table[bucketIndex] = new Entry<K,V>(hash, key, value, e); if (size++ >= threshold) resize(2 * table.length); } </pre> <br /> <br /> 而每当hashMap扩容后,内部的每个元素存攄位置都会发生变化Q因为元素的最l位|是其hashCode对keyI间长度取模而得Q,因此resizeҎ(gu)中又会调用transfer函数Q用来重新分配内部的元素Q这个过E成为rehashQ是十分消耗性能的,因此在可预知元素的个数的情况下,一般应该避免用缺省的initialCapacityQ而是通过构造函Cؓ其指定一个倹{例如我们可能会惌数据库查询所?000条记录以某个特定字段Q比如IDQؓkey~存在hashMap中,Z提高效率、避免rehashQ可以直接指定initialCapacity?048?<br /> <br /> 另一个值得注意的地Ҏ(gu)QhashMap其keyI间的长度一定ؓ2的Nơ方Q这一点可以从一下源码中看出来: <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools">Java代码Q?/div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">int</span><span> capacity = </span><span id="wmqeeuq" class="number">1</span><span>;   </span></span> <li><span id="wmqeeuq" class="keyword">while</span><span> (capacity < initialCapacity)    </span></span> <li><span>    capacity <<= </span><span id="wmqeeuq" class="number">1</span><span>;  </span></span> </li> </ol> </div> <pre class="java" style="display: none" name="code">int capacity = 1; while (capacity < initialCapacity) capacity <<= 1; </pre> <br /> <br /> 即我们在构造函C指定的initialCapacity不是2的^Ҏ(gu)Qcapacityq是会被赋gؓ2的Nơ方?<br /> <br /> Z么Sun Microsystem的工E师要将hashMap keyI间的长度设?的Nơ方呢?q里参考R.W.Floyedl出的衡量散列思想的三个标准: <br /> <br /> <ul><br /> 一个好的hash法的计应该是非常快的 <br /> 一个好的hash法应该是冲H极化 <br /> 如果存在冲突,应该是冲H均匀?<br /> </ul> <br /> <br /> Z各元素的hashCode保存至长度ؓLength的key数组中,一般采用取模的方式Q即index = hashCode % Length。不可避免的Q存在多个不同对象的hashCode被安排在同一位置Q这是我们qx所谓的“冲突”。如果仅仅是考虑元素均匀化与冲突极小化,g应该Length取ؓ素数Q尽没有明昄理论来支持这一点,但数学家们通过大量的实践得出结论,对素数取模的产生l果的无x要大于其它数字Q。ؓ此,Craig Larman and Rhett Guthrie《Java Performence》中Ҏ(gu)也大加抨凅Rؓ了弄清楚q个问题QBruce EckelQThinking in JAVA的作者)专程采访了java.util.hashMap的作者Joshua BlochQƈ他采用q种设计的原因放C|上Q?a target=_blank><em>http://www.roseindia.net/javatutorials/javahashmap.shtml</em></a>Q??<br /> <br /> 上述设计的原因在于,取模q算在包括JAVA在内的大多数语言中的效率都十分低下,而当除数?的Nơ方Ӟ取模q算退化ؓ最单的位运,其效率明显提升(按照Bruce Eckell出的数据,大约可以提升5?倍) 。看看JDK中是如何实现的: <br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools">Java代码Q?/div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">static</span><span> </span><span id="wmqeeuq" class="keyword">int</span><span> indexFor(</span><span id="wmqeeuq" class="keyword">int</span><span> h, </span><span id="wmqeeuq" class="keyword">int</span><span> length) {   </span></span> <li><span>    </span><span id="wmqeeuq" class="keyword">return</span><span> h & (length-</span><span id="wmqeeuq" class="number">1</span><span>);   </span></span> <li><span>}  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">static int indexFor(int h, int length) { return h & (length-1); } </pre> <br /> <br /> 当keyI间长度?的Nơ方Ӟ计算hashCode为h的元素的索引可以用简单的与操作来代替W拙的取模操作!假设某个对象的hashCode?5Q二q制?00011Q,而hashMap采用默认的initialCapacityQ?6Q,那么indexFor计算所得结果将会是100011 & 1111 = 11Q即十进制的3Q是不是恰好?5 Mod 16?<br /> <br /> 上面的方法有一个问题,是它的计算l果仅有对象hashCode的低位决定,而高位被l统屏蔽了;以上面ؓ例,19Q?0011Q?5Q?00011Q?7Q?000011Q等具有相同的l果。针对这个问题, Joshua Bloch采用?#8220;防M性编E?#8221;的解x法,在用各对象的hashCode之前对其q行二次HashQ参看JDK中的源码Q?<br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">static</span><span> </span><span id="wmqeeuq" class="keyword">int</span><span> hash(Object x) {   </span></span> <li><span>        </span><span id="wmqeeuq" class="keyword">int</span><span> h = x.hashCode();   </span></span> <li><span>        h += ~(h << </span><span id="wmqeeuq" class="number">9</span><span>);   </span></span> <li><span>        h ^=  (h >>> </span><span id="wmqeeuq" class="number">14</span><span>);   </span></span> <li><span>        h +=  (h << </span><span id="wmqeeuq" class="number">4</span><span>);   </span></span> <li><span>        h ^=  (h >>> </span><span id="wmqeeuq" class="number">10</span><span>);   </span></span> <li><span>        </span><span id="wmqeeuq" class="keyword">return</span><span> h;   </span></span> <li><span>    }  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">static int hash(Object x) { int h = x.hashCode(); h += ~(h << 9); h ^= (h >>> 14); h += (h << 4); h ^= (h >>> 10); return h; } </pre> <br /> <br /> 采用q种旋{Hash函数的主要目的是让原有hashCode的高位信息也能被充分利用Q且兼顾计算效率以及数据l计的特性,其具体的原理已超Z本文的领域?<br /> <br /> 加快Hash效率的另一个有效途径是编写良好的自定义对象的HashCodeQString的实现采用了如下的计方法: <br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">for</span><span> (</span><span id="wmqeeuq" class="keyword">int</span><span> i = </span><span id="wmqeeuq" class="number">0</span><span>; i < len; i++) {   </span></span> <li><span>h = </span><span id="wmqeeuq" class="number">31</span><span>*h + val[off++];   </span></span> <li><span>}   </span> <li><span>hash = h;  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">for (int i = 0; i < len; i++) { h = 31*h + val[off++]; } hash = h; </pre> <br /> <br /> q种Ҏ(gu)HashCode的计方法可能最早出现在Brian W. Kernighan和Dennis M. Ritchie的《The C Programming Language》中Q被认ؓ是性h(hun)比最高的法Q又被称为times33法Q因为C中乘数常量ؓ33QJAVA中改?1Q,实际上,包括List在内的大多数的对象都是用q种Ҏ(gu)计算Hash倹{?<br /> <br /> 另一U比较特D的hash法UCؓ布隆qo器,它以牺牲l微_ֺZP换来存储I间的大量节俭,常用于诸如判断用户名重复、是否在黑名单上{等Q可以参考李开复的数学之美pdW?3(<em><a target="_blank">http://googlechinablog.com/2006/08/blog-post.html</a></em>Q?<br /> <br /> <span style="font-size: large"><strong>Fail-Fast机制</strong></span> <br /> <br /> 众所周知QHashMap不是U程安全的集合类。但在某些容错能力较好的应用中,如果你不想仅仅因?%的可能性而去承受hashTable的同步开销Q则可以考虑利用一下HashMap的Fail-Fast机制Q其具体实现如下Q?<br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span>Entry<K,V> nextEntry() {    </span></span> <li><span id="wmqeeuq" class="keyword">if</span><span> (modCount != expectedModCount)   </span></span> <li><span>    </span><span id="wmqeeuq" class="keyword">throw</span><span> </span><span id="wmqeeuq" class="keyword">new</span><span> ConcurrentModificationException();   </span></span> <li><span>                     ……   </span> <li><span>}  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">Entry<K,V> nextEntry() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); …… } </pre> <br /> <br /> 其中modCount为HashMap的一个实例变量,q且被声明ؓvolatileQ表CZQ何线E都可以看到该变量被其它U程修改的结果(Ҏ(gu)JVM内存模型的优化,每一个线E都会存一份自q工作内存Q此工作内存的内容与本地内存q时时d都同步,因此可能会出现线E间的修改不可见的问题) 。用Iterator开始P代时Q会modCount的赋值给expectedModCountQ在q代q程中,通过每次比较两者是否相{来判断HashMap是否在内部或被其它线E修攏VHashMap的大多数修改Ҏ(gu)都会改变ModCountQ参考下面的源码Q?<br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">public</span><span> V put(K key, V value) {   </span></span> <li><span>    K k = maskNull(key);   </span> <li><span>        </span><span id="wmqeeuq" class="keyword">int</span><span> hash = hash(k);   </span></span> <li><span>        </span><span id="wmqeeuq" class="keyword">int</span><span> i = indexFor(hash, table.length);   </span></span> <li><span>        </span><span id="wmqeeuq" class="keyword">for</span><span> (Entry<K,V> e = table[i]; e != </span><span id="wmqeeuq" class="keyword">null</span><span>; e = e.next) {   </span></span> <li><span>            </span><span id="wmqeeuq" class="keyword">if</span><span> (e.hash == hash && eq(k, e.key)) {   </span></span> <li><span>                V oldValue = e.value;   </span> <li><span>                e.value = value;   </span> <li><span>                e.recordAccess(</span><span id="wmqeeuq" class="keyword">this</span><span>);   </span></span> <li><span>                </span><span id="wmqeeuq" class="keyword">return</span><span> oldValue;   </span></span> <li><span>            }   </span> <li><span>        }   </span> <li><span>        modCount++;   </span> <li><span>        addEntry(hash, k, value, i);   </span> <li><span>        </span><span id="wmqeeuq" class="keyword">return</span><span> </span><span id="wmqeeuq" class="keyword">null</span><span>;   </span></span> <li><span>    }  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">public V put(K key, V value) { K k = maskNull(key); int hash = hash(k); int i = indexFor(hash, table.length); for (Entry<K,V> e = table[i]; e != null; e = e.next) { if (e.hash == hash && eq(k, e.key)) { V oldValue = e.value; e.value = value; e.recordAccess(this); return oldValue; } } modCount++; addEntry(hash, k, value, i); return null; }</pre> <br /> <br /> 以putҎ(gu)ZQ每ơ往HashMap中添加元素都会导致modCount自增。其它诸如remove、clearҎ(gu)也都包含cM的操作?<br /> 从上面可以看出,HashMap所采用的Fail-Fast机制本质上是一U乐观锁机制Q通过查状态——没有问题则忽略——有问题则抛出异常的方式Q来避免U程同步的开销Q下面给Z个在单线E环境下发生Fast-Fail的例子: <br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">class</span><span> Test {     </span></span> <li><span>    </span><span id="wmqeeuq" class="keyword">public</span><span> </span><span id="wmqeeuq" class="keyword">static</span><span> </span><span id="wmqeeuq" class="keyword">void</span><span> main(String[] args) {                </span></span> <li><span>        java.util.HashMap<Object,String> map=</span><span id="wmqeeuq" class="keyword">new</span><span> java.util.HashMap<Object,String>();     </span></span> <li><span>       map.put(</span><span id="wmqeeuq" class="keyword">new</span><span> Object(), </span><span id="wmqeeuq" class="string">"a"</span><span>);     </span></span> <li><span>       map.put(</span><span id="wmqeeuq" class="keyword">new</span><span> Object(), </span><span id="wmqeeuq" class="string">"b"</span><span>);     </span></span> <li><span>       java.util.Iterator<Object> it=map.keySet().iterator();     </span> <li><span>       </span><span id="wmqeeuq" class="keyword">while</span><span>(it.hasNext()){     </span></span> <li><span>           it.next();     </span> <li><span>           map.put(</span><span id="wmqeeuq" class="string">""</span><span>, </span><span id="wmqeeuq" class="string">""</span><span>);          </span></span> <li><span>        System.out.println(map.size());     </span> <li><span>    }     </span> <li><span>}  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">class Test { public static void main(String[] args) { java.util.HashMap<Object,String> map=new java.util.HashMap<Object,String>(); map.put(new Object(), "a"); map.put(new Object(), "b"); java.util.Iterator<Object> it=map.keySet().iterator(); while(it.hasNext()){ it.next(); map.put("", ""); System.out.println(map.size()); } }</pre> <br /> <br /> q行上面的代码会抛出java.util.ConcurrentModificationExceptionQ因为在q代q程中修改了HashMap内部的元素导致modCount自增。若上面代码中 map.put(new Object(), "b") q句注释掉,E序会顺利通过Q因为此时HashMap中只包含一个元素,l过一ơP代后已到了尾部,所以不会出现问题,也就没有抛出异常的必要了?<br /> 在通常q发环境下,q是采用同步机制。这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在q样的对象,则应该?Collections.synchronizedMap Ҏ(gu)?#8220;包装”该映。最好在创徏时完成这一操作Q以防止意外的非同步讉K?<br /> <br /> <span style="font-size: large"><strong>LinkedHashMap</strong></span> <br /> <br /> 遍历HashMap所得到的数据是杂ؕ无章的,q在某些情况下客户需要特定遍历顺序时是十分有用的。比如,q种数据l构很适合构徏 LRU ~存。调?put ?get Ҏ(gu)会讉K相应的条目(假定调用完成后它q存在)。putAll Ҏ(gu)以指定映的条目集合q代器提供的?值映关pȝ序Qؓ指定映射的每个映关pȝ成一个条目访问。Sun提供的J2SE说明文档特别规定M其他Ҏ(gu)均不生成条目讉KQ尤Ӟcollection 集合cȝ操作不会影响底层映射的P代顺序?<br /> <br /> LinkedHashMap的实C HashMap 的不同之处在于,前者维护着一个运行于所有条目的双重链接列表。此链接列表定义了P代顺序,该P代顺序通常是集合中元素的插入序。该cd义了header、before与after三个属性来表示该集合类的头与前?#8220;指针”Q其具体用法cM于数据结构中的双链表Q以删除某个元素ZQ?<br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">private</span><span> </span><span id="wmqeeuq" class="keyword">void</span><span> remove() {   </span></span> <li><span>       before.after = after;   </span> <li><span>       after.before = before;   </span> <li><span>}  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">private void remove() { before.after = after; after.before = before; }</pre> <br /> <br /> 实际上就是改变前后指针所指向的元素?<br /> <br /> 昄Q由于增加了l护链接列表的开支,其性能要比 HashMap E逊一{,不过有一点例外:LinkedHashMap的P代所需旉与其的所包含的元素成比例Q而HashMap q代旉很可能开支较大,因ؓ它所需要的旉与其定wQ分配给KeyI间的长度)成比例。一a以蔽之,随机存取用HashMapQ顺序存取或是遍历用LinkedHashMap?<br /> <br /> LinkedHashMapq重写了removeEldestEntryҎ(gu)以实现自动清除过期数据的功能Q这在HashMap中是无法实现的,因ؓ后者其内部的元素是无序的。默认情况下QLinkedHashMap中的removeEldestEntry的作用被关闭Q其具体实现如下Q?<br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">protected</span><span> </span><span id="wmqeeuq" class="keyword">boolean</span><span> removeEldestEntry(Map.Entry<K,V> eldest) {   </span></span> <li><span>    </span><span id="wmqeeuq" class="keyword">return</span><span> </span><span id="wmqeeuq" class="keyword">false</span><span>;   </span></span> <li><span>}  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">protected boolean removeEldestEntry(Map.Entry<K,V> eldest) { return false; }</pre> <br /> <br /> 可以使用如下的代码覆盖removeEldestEntryQ?<br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">private</span><span> </span><span id="wmqeeuq" class="keyword">static</span><span> </span><span id="wmqeeuq" class="keyword">final</span><span> </span><span id="wmqeeuq" class="keyword">int</span><span> MAX_ENTRIES = </span><span id="wmqeeuq" class="number">100</span><span>;   </span></span> <li><span>  </span> <li><span id="wmqeeuq" class="keyword">protected</span><span> </span><span id="wmqeeuq" class="keyword">boolean</span><span> removeEldestEntry(Map.Entry eldest) {   </span></span> <li><span>    </span><span id="wmqeeuq" class="keyword">return</span><span> size() > MAX_ENTRIES;   </span></span> <li><span>}  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">private static final int MAX_ENTRIES = 100; protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; } </pre> <br /> <br /> 它表C,刚开始,LinkedHashMap中的元素不断增长Q当它内部的元素过MAX_ENTRIESQ?00Q后Q每当有新的元素被插入时Q都会自动删除双链表中最前端Q最旧)的元素,从而保持LinkedHashMap的长度稳定?<br /> <br /> ~省情况下,LinkedHashMap采取的更新策略是cM于队列的FIFOQ如果你惛_现更复杂的更新逻辑比如LRUQ最q最用) {,可以在构造函C指定其accessOrder为trueQ因为的讉K元素的方法(getQ内部会调用一?#8220;钩子”Q即recordAccessQ其具体实现如下Q?<br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">void</span><span> recordAccess(HashMap<K,V> m) {   </span></span> <li><span>    LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;   </span> <li><span>    </span><span id="wmqeeuq" class="keyword">if</span><span> (lm.accessOrder) {   </span></span> <li><span>        lm.modCount++;   </span> <li><span>        remove();   </span> <li><span>        addBefore(lm.header);   </span> <li><span>    }   </span> <li><span>}  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">void recordAccess(HashMap<K,V> m) { LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m; if (lm.accessOrder) { lm.modCount++; remove(); addBefore(lm.header); } }</pre> <br /> <br /> 上述代码主要实现了这L功能Q如果accessOrder被设|ؓtrueQ则每次讉K元素Ӟ都将该元素移至headr的前面,即链表的N。将removeEldestEntry与accessOrder一起用,可以实现最基本的内存缓存,具体代码可参?a target=_blank><em>http://bluepopopo.javaeye.com/blog/180236</em></a>?<br /> <br /> <span style="font-size: large"><strong>WeakHashMap</strong></span> <br /> <br /> 99%的JAVA教材教导我们不要d预JVM的垃圑֛收机Ӟ但JAVA中确实存在着与其密切相关的四U引用:强引用、Y引用、弱引用以及q象引用?<br /> <br /> JAVA中默认的HashMap采用的是采用cM于强引用的强键来理的,q意味着即作ؓkey的对象已l不存在了(指没有Q何一个引用指向它Q,也仍然会保留在HashMap中,在某些情况下Q例如内存缓存)中,q些q期的条目可能会造成内存泄漏{问题?<br /> <br /> WeakHashMap采用的策略是Q只要作为key的对象已l不存在了(出生命周期Q,׃会阻止垃圾收集器清空此条目,即当前机器的内存ƈ不紧张。不q,׃GC是一个优先很低的线E,因此不一定会很快发现那些只具有弱引用的对象,除非你显C地调用它,可以参考下面的例子Q?<br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">public</span><span> </span><span id="wmqeeuq" class="keyword">static</span><span> </span><span id="wmqeeuq" class="keyword">void</span><span> main(String[] args) {   </span></span> <li><span>    Map<String, String>map = </span><span id="wmqeeuq" class="keyword">new</span><span> WeakHashMap<String, String>();   </span></span> <li><span>    map.put(</span><span id="wmqeeuq" class="keyword">new</span><span> String(</span><span id="wmqeeuq" class="string">"Alibaba"</span><span>), </span><span id="wmqeeuq" class="string">"alibaba"</span><span>);   </span></span> <li><span>    </span><span id="wmqeeuq" class="keyword">while</span><span> (map.containsKey(</span><span id="wmqeeuq" class="string">"Alibaba"</span><span>)) {   </span></span> <li><span>        </span><span id="wmqeeuq" class="keyword">try</span><span> {   </span></span> <li><span>            Thread.sleep(</span><span id="wmqeeuq" class="number">500</span><span>);   </span></span> <li><span>         } </span><span id="wmqeeuq" class="keyword">catch</span><span> (InterruptedException ignored) {   </span></span> <li><span>         }   </span> <li><span>         System.out.println(</span><span id="wmqeeuq" class="string">"Checking for empty"</span><span>);   </span></span> <li><span>         System.gc();   </span> <li><span>    }  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">public static void main(String[] args) { Map<String, String>map = new WeakHashMap<String, String>(); map.put(new String("Alibaba"), "alibaba"); while (map.containsKey("Alibaba")) { try { Thread.sleep(500); } catch (InterruptedException ignored) { } System.out.println("Checking for empty"); System.gc(); } </pre> <br /> <br /> 上述代码输出一ơChecking for empty退ZȝE,意味着GC在最q的一ơ垃圑֛收周期中清除了new String(“Alibaba”),同时WeakHashMap也做Z及时的反应,该键对应的条目删除了。如果将map的类型改为HashMap的话Q由于其内部采用的是强引用机Ӟ因此即GC被显C用,map中的条目依然存在Q程序会不断地打出Checking for empty字样。另外,在用WeakHashMap的情况下Q若是将 <br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span>map.put(</span><span id="wmqeeuq" class="keyword">new</span><span> String(</span><span id="wmqeeuq" class="string">"Alibaba"</span><span>), </span><span id="wmqeeuq" class="string">"alibaba"</span><span>);   </span></span> </li> </ol> </div> <pre class="java" style="display: none" name="code">map.put(new String("Alibaba"), "alibaba"); </pre> <br /> <br /> 改ؓ <br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span>map.put(</span><span id="wmqeeuq" class="string">"Alibaba"</span><span>, </span><span id="wmqeeuq" class="string">"alibaba"</span><span>);   </span></span> </li> </ol> </div> <pre class="java" style="display: none" name="code">map.put("Alibaba", "alibaba"); </pre> <br /> <br /> E序q是会不断输出Checking for empty。这与前面我们分析的WeakHashMap的弱引用机制q不矛盾Q因为JVMZ减小重复创徏和维护多个相同String的开销Q其内部采用了蝇量模式(《JAVA与模式》)Q此时的“Alibaba”是存攑֜帔R池而非堆中的,因此即没有对象指向“Alibaba”Q它也不会被GC回收。弱引用特别适合以下对象Q占用大量内存,但通过垃圾回收功能回收以后很容易重新创建?<br /> <br /> 介于HashMap和WeakHashMap之中的是SoftHashMapQ它所采用的Y引用的策略指的是Q垃圾收集器q不像其攉弱可及的对象一样尽量地攉软可及的对象Q相反,它只在真?“需?#8221; 内存时才攉软可及的对象。Y引用对于垃圾攉器来说是一U?#8220;睁一只眼Q闭一只眼”方式Q即 “只要内存不太紧张Q我׃保留该对象。但是如果内存变得真正紧张了Q我׃L集ƈ处理q个对象?#8221; p一点看Q它其实要比WeakHashMap更适合于实现缓存机制。遗憄是,JAVA中ƈ没有实现相关的SoftHashMapc(Apache和Google提供了第三方的实玎ͼQ但它却是提供了两个十分重要的类java.lang.ref.SoftReference以及ReferenceQueueQ可以在对象应用状态发生改变是得到通知Q可以参考com.alibaba.common.collection.SofthashMap中processQueueҎ(gu)的实? <br /> <br /> <div id="wmqeeuq" class="dp-highlighter"> <div id="wmqeeuq" class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" >Java代码Q?/a></div> <ol class="dp-j"> <li><span><span id="wmqeeuq" class="keyword">private</span><span> ReferenceQueue queue = </span><span id="wmqeeuq" class="keyword">new</span><span> ReferenceQueue();   </span></span> <li><span>ValueCell vc;   </span> <li><span>Map hash = </span><span id="wmqeeuq" class="keyword">new</span><span> HashMap(initialCapacity, loadFactor);   </span></span> <li><span>……   </span> <li><span id="wmqeeuq" class="keyword">while</span><span> ((vc = (ValueCell) queue.poll()) != </span><span id="wmqeeuq" class="keyword">null</span><span>) {   </span></span> <li><span id="wmqeeuq" class="keyword">if</span><span> (vc.isValid()) {   </span></span> <li><span>          hash.remove(vc.key);   </span> <li><span>           } </span><span id="wmqeeuq" class="keyword">else</span><span> {   </span></span> <li><span>             valueCell.dropped--;   </span> <li><span>           }   </span> <li><span>}   </span> <li><span>}  </span> </li> </ol> </div> <pre class="java" style="display: none" name="code">private ReferenceQueue queue = new ReferenceQueue(); ValueCell vc; Map hash = new HashMap(initialCapacity, loadFactor); …… while ((vc = (ValueCell) queue.poll()) != null) { if (vc.isValid()) { hash.remove(vc.key); } else { valueCell.dropped--; } } }</pre> <br /> <br /> processQueueҎ(gu)会在几乎所有SoftHashMap的方法中被调用到QJVM会通过ReferenceQueue的pollҎ(gu)通知该对象已l过期ƈ且当前的内存现状需要将它释放,此时我们可以将其从hashMap中剔除。事实上Q默认情况下QAlibaba的MemoryCache所使用的就是SoftHashMap?<br /> <br /> 来源Q?a >http://grunt1223.javaeye.com/blog/544497</a><br /> <img src ="http://www.aygfsteel.com/ArcticOcean/aggbug/328316.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.aygfsteel.com/ArcticOcean/" target="_blank">Cool Jazz</a> 2010-08-09 15:14 <a href="http://www.aygfsteel.com/ArcticOcean/archive/2010/08/09/328316.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss> <footer> <div class="friendship-link"> <a href="http://www.aygfsteel.com/" title="狠狠久久亚洲欧美专区_中文字幕亚洲综合久久202_国产精品亚洲第五区在线_日本免费网站视频">狠狠久久亚洲欧美专区_中文字幕亚洲综合久久202_国产精品亚洲第五区在线_日本免费网站视频</a> </div> </footer> վ֩ģ壺 <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">»</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">е</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">̩</a>| <a href="http://" target="_blank">ˮ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ɽ</a>| <a href="http://" target="_blank">֯</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">˳</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">°Ͷ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ɳƺ</a>| <a href="http://" target="_blank">Ĭ</a>| <a href="http://" target="_blank">ƺ</a>| <a href="http://" target="_blank">ͼ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ľ</a>| <a href="http://" target="_blank">ͬ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">Ľ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">˳</a>| <a href="http://" target="_blank">ƽ</a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank"></a>| <a href="http://" target="_blank">ɽ</a>| <a href="http://" target="_blank">쳤</a>| <a href="http://" target="_blank">ͨ</a>| <a href="http://" target="_blank">ͨ</a>| <a href="http://" target="_blank"></a>| <script> (function(){ var bp = document.createElement('script'); var curProtocol = window.location.protocol.split(':')[0]; if (curProtocol === 'https') { bp.src = 'https://zz.bdstatic.com/linksubmit/push.js'; } else { bp.src = 'http://push.zhanzhang.baidu.com/push.js'; } var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })(); </script> </body>