??xml version="1.0" encoding="utf-8" standalone="yes"?>色偷偷噜噜噜亚洲男人,亚洲综合精品一二三区在线,国产亚洲视频在线http://www.tkk7.com/CarpenterLee/技术只是工P重要的是人才Q?/description>zh-cnFri, 09 May 2025 14:24:13 GMTFri, 09 May 2025 14:24:13 GMT60Java Stream API入门?/title><link>http://www.tkk7.com/CarpenterLee/archive/2017/03/29/432420.html</link><dc:creator>CarpenterLee</dc:creator><author>CarpenterLee</author><pubDate>Wed, 29 Mar 2017 13:38:00 GMT</pubDate><guid>http://www.tkk7.com/CarpenterLee/archive/2017/03/29/432420.html</guid><wfw:comment>http://www.tkk7.com/CarpenterLee/comments/432420.html</wfw:comment><comments>http://www.tkk7.com/CarpenterLee/archive/2017/03/29/432420.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/CarpenterLee/comments/commentRss/432420.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/CarpenterLee/services/trackbacks/432420.html</trackback:ping><description><![CDATA[<div class="0e8yco0" id="cnblogs_post_body" class="cnblogs-markdown"> <p><a >本文github地址</a><br /> 你可能没意识到Java对函数式~程的重视程度,看看Java 8加入函数式编E扩充多功能就清楚了。Java 8之所以费q么大功夫引入函数式~程Q原因有二:</p> <ol> <li><strong>代码z?/strong>Q函数式~程写出的代码简z且意图明确Q?em>stream</em>接口让你从此告别<em>for</em>循环?/li> <li><strong>多核友好</strong>QJava函数式编E得编写ƈ行程序从未如此简单,你需要的全部是调用一?code>parallel()</code>Ҏ?/li> </ol> <p>q一节我们学?em>stream</em>Q也是Java函数式编E的主角。对于Java 7来说<em>stream</em>完全是个陌生东西Q?em>stream</em>q不是某U数据结构,它只是数据源的一U视图。这里的数据源可以是一个数l,Java容器或I/O channel{。正因如此要得到一?em>stream</em>通常不会手动创徏Q而是调用对应的工h法,比如Q?/p> <ul> <li>调用<code>Collection.stream()</code>或?code>Collection.parallelStream()</code>Ҏ</li> <li>调用<code>Arrays.stream(T[] array)</code>Ҏ</li> </ul> <p>常见?em>stream</em>接口l承关系如图Q?/p> <p><img src="http://images2015.cnblogs.com/blog/939998/201703/939998-20170313215540823-221594903.png" width="500px" align="left" alt="Java_stream_Interfaces" /></p> <p>图中4U?em>stream</em>接口l承?code>BaseStream</code>Q其?code>IntStream, LongStream, DoubleStream</code>对应三种基本cdQ?code>int, long, double</code>Q注意不是包装类型)Q?code>Stream</code>对应所有剩余类型的<em>stream</em>视图。ؓ不同数据cd讄不同<em>stream</em>接口Q可?.提高性能Q?.增加特定接口函数?/p> <p><br /> </p> <p><img src="http://images2015.cnblogs.com/blog/939998/201703/939998-20170313215634385-204854577.png" width="400px" align="right" alt="WRONG_Java_stream_Interfaces" /></p> <p>你可能会奇怪ؓ什么不?code>IntStream</code>{设计成<code>Stream</code>的子接口Q毕竟这接口中的Ҏ名大部分是一L。答案是q些Ҏ的名字虽然相同,但是q回cd不同Q如果设计成父子接口关系Q这些方法将不能共存Q因为Java不允许只有返回类型不同的Ҏ重蝲?/p> <p>虽然大部分情况下<em>stream</em>是容器调?code>Collection.stream()</code>Ҏ得到的,?em>stream</em>?em>collections</em>有以下不同:</p> <ul> <li><strong>无存?/strong>?em>stream</em>不是一U数据结构,它只是某U数据源的一个视图,数据源可以是一个数l,Java容器或I/O channel{?/li> <li><strong>为函数式~程而生</strong>。对<em>stream</em>的Q何修攚w不会修改背后的数据源Q比如对<em>stream</em>执行qo操作q不会删除被qo的元素,而是会生一个不包含被过滤元素的?em>stream</em>?/li> <li><strong>惰式执行</strong>?em>stream</em>上的操作q不会立x行,只有{到用户真正需要结果的时候才会执行?/li> <li><strong>可消Ҏ?/strong>?em>stream</em>只能?#8220;消费”一ơ,一旦遍历过׃失效Q就像容器的q代器那P惌再次遍历必须重新生成?/li> </ul> <p>?em>stream</em>的操作分Zؓ两类Q?strong>中间操作(<em>intermediate operations</em>)和结束操?<em>terminal operations</em>)</strong>Q二者特ҎQ?/p> <ol> <li><strong>中间操作L会惰式执?/strong>Q调用中间操作只会生成一个标C该操作的?em>stream</em>Q仅此而已?/li> <li><strong>l束操作会触发实际计?/strong>Q计发生时会把所有中间操作积攒的操作?em>pipeline</em>的方式执行,q样可以减少q代ơ数。计完成之?em>stream</em>׃失效?/li> </ol> <p>如果你熟悉Apache Spark RDDQ对<em>stream</em>的这个特点应该不陌生?/p> <p>下表汇M<code>Stream</code>接口的部分常见方法:</p> <table> <thead> <tr class="header"> <th>操作cd</th><th>接口Ҏ</th> </tr> </thead> <tbody> <tr class="odd"> <td>中间操作</td> <td>concat() distinct() filter() flatMap() limit() map() peek() <br /> skip() sorted() parallel() sequential() unordered()</td> </tr> <tr class="even"> <td>l束操作</td> <td>allMatch() anyMatch() collect() count() findAny() findFirst() <br /> forEach() forEachOrdered() max() min() noneMatch() reduce() toArray()</td> </tr> </tbody> </table> <p>区分中间操作和结束操作最单的ҎQ就是看Ҏ的返回|q回gؓ<em>stream</em>的大都是中间操作Q否则是l束操作?/p> <h2 id="streamҎ使用">streamҎ使用</h2> <p><em>stream</em>跟函数接口关p非常紧密,没有函数接口<em>stream</em>无法工作。回一下:<strong>函数接口是指内部只有一个抽象方法的接口</strong>。通常函数接口出现的地斚w可以使用Lambda表达式,所以不必记忆函数接口的名字?/p> <h3 id="foreach">forEach()</h3> <p>我们?code>forEach()</code>Ҏq不陌生Q在<code>Collection</code>中我们已l见q。方法签名ؓ<code>void forEach(Consumer<? super E> action)</code>Q作用是对容器中的每个元素执?code>action</code>指定的动作,也就是对元素q行遍历?/p> <div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --><span style="color: #008000; ">//</span><span style="color: #008000; "> 使用Stream.forEach()q代</span><span style="color: #008000; "><br /> </span>Stream<String> stream = Stream.of("I", "love", "you", "too");<br /> stream.forEach(str -> System.out.println(str));</div> <p><br /> ׃<code>forEach()</code>是结束方法,上述代码会立x行,输出所有字W串?/p> <h3 id="filter">filter()</h3> <p><img src="http://images2015.cnblogs.com/blog/939998/201703/939998-20170313215744635-60161700.png" width="250px" align="right" hspace="10px" alt="Stream filter" /></p> <p>函数原型?code>Stream<T> filter(Predicate<? super T> predicate)</code>Q作用是q回一个只包含满<code>predicate</code>条g元素?code>Stream</code>?br /> </p> <div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> --><span style="color: #008000; ">//</span><span style="color: #008000; "> 保留长度{于3的字W串</span><span style="color: #008000; "><br /> </span>Stream<String> stream= Stream.of("I", "love", "you", "too");<br /> stream.filter(str -> str.length()==3)<br />     .forEach(str -> System.out.println(str));</div> <div id="8uc6q00" class="sourceCode"> <pre class="sourceCode java"><span style="font-family: verdana, 'courier new';">上述代码输Zؓ长度{于3的字W串</span><code>you</code><span style="font-family: verdana, 'courier new';">?/span><code>too</code><span style="font-family: verdana, 'courier new';">。注意,׃</span><code>filter()</code><span style="font-family: verdana, 'courier new';">是个中间操作Q如果只调用</span><code>filter()</code><span style="font-family: verdana, 'courier new';">不会有实际计,因此也不会输ZQ何信息?/span></pre> </div> <h3 id="distinct">distinct()</h3> <p><img src="http://images2015.cnblogs.com/blog/939998/201703/939998-20170313215820213-2027403065.png" width="200px" align="left" hspace="10px" alt="Stream distinct" /></p> <p>函数原型?code>Stream<T> distinct()</code>Q作用是q回一个去除重复元素之后的<code>Stream</code>?/p> <div id="q08skkc" class="sourceCode"> <pre class="sourceCode java"><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->Stream<String> stream= Stream.of("I", "love", "you", "too", "too"); stream.distinct()     .forEach(str -> System.out.println(str));</div> </pre> </div> <p>上述代码会输出去掉一?code>too</code>之后的其余字W串?/p> <p><br /></p><h3 id="sorted"> sorted()</h3> <p>排序函数有两个,一个是用自焉序排序,一个是使用自定义比较器排序Q函数原型分别ؓ<code>Stream<T> sorted()</code>?code>Stream<T> sorted(Comparator<? super T> comparator)</code>?/p> <div id="g0aqoww" class="sourceCode"> <pre class="sourceCode java"><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->Stream<String> stream= Stream.of("I", "love", "you", "too"); stream.sorted((str1, str2) -> str1.length()-str2.length())     .forEach(str -> System.out.println(str));</div> </pre> </div> <p>上述代码输出按照长度升序排序后的字W串Q结果完全在预料之中?/p> <h3 id="map">map()</h3> <p><img src="http://images2015.cnblogs.com/blog/939998/201703/939998-20170313215854807-2125107413.png" width="250px" align="right" hspace="10px" alt="Stream map" /></p> <p>函数原型?code><R> Stream<R> map(Function<? super T,? extends R> mapper)</code>Q作用是q回一个对当前所有元素执行执?code>mapper</code>之后的结果组成的<code>Stream</code>。直观的_是Ҏ个元素按照某U操作进行{换,转换前后<code>Stream</code>中元素的个数不会改变Q但元素的类型取决于转换之后的类型?/p> <div id="g8i8w6m" class="sourceCode"> <pre class="sourceCode java"><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->Stream<String> stream = Stream.of("I", "love", "you", "too"); stream.map(str -> str.toUpperCase())     .forEach(str -> System.out.println(str));</div> </pre> </div> <p>上述代码输出原字符串的大写形式?/p> <h3 id="flatmap">flatMap()</h3> <p><img src="http://images2015.cnblogs.com/blog/939998/201703/939998-20170313215941870-1457129851.png" width="300px" align="left" hspace="10px" alt="Stream flatMap" /></p> <p>函数原型?code><R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)</code>Q作用是Ҏ个元素执?code>mapper</code>指定的操作,q用所?code>mapper</code>q回?code>Stream</code>中的元素l成一个新?code>Stream</code>作ؓ最l返回结果。说h太拗口,通俗的讲<code>flatMap()</code>的作用就相当于把?em>stream</em>中的所有元素都"摊^"之后l成?code>Stream</code>Q{换前后元素的个数和类型都可能会改变?/p> <div id="ymcew0g" class="sourceCode"> <pre class="sourceCode java"><div style="font-size: 13px; border: 1px solid #cccccc; padding: 4px 5px 4px 4px; width: 98%; word-break: break-all; background-color: #eeeeee;"><!--<br /> <br /> Code highlighting produced by Actipro CodeHighlighter (freeware)<br /> http://www.CodeHighlighter.com/<br /> <br /> -->Stream<List<Integer>> stream = Stream.of(Arrays.asList(1,2), Arrays.asList(3, 4, 5));<br />stream.flatMap(list -> list.stream())<br />    .forEach(i -> System.out.println(i));</div> </pre> </div> <p>上述代码中,原来?code>stream</code>中有两个元素Q分别是两个<code>List<Integer></code>Q执?code>flatMap()</code>之后Q将每个<code>List</code>?#8220;摊^”成了一个个的数字,所以会C生一个由5个数字组成的<code>Stream</code>。所以最l将输出1~5q?个数字?/p> <h2 id="l语">l语</h2> <p>截止到目前我们感觉良好,已介l?code>Stream</code>API理解hq不费劲ѝ如果你此以ؓ函数式编E不q如此,恐怕是高兴地太早了。下一节对<a ><code>Stream</code>规约操作</a>的介l将h你现在的认识?/p> <p><a >本文github地址</a>Q欢q关注?/p> </div><img src ="http://www.tkk7.com/CarpenterLee/aggbug/432420.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/CarpenterLee/" target="_blank">CarpenterLee</a> 2017-03-29 21:38 <a href="http://www.tkk7.com/CarpenterLee/archive/2017/03/29/432420.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>《深入理解Java集合框架》系列文?/title><link>http://www.tkk7.com/CarpenterLee/archive/2016/05/31/430716.html</link><dc:creator>CarpenterLee</dc:creator><author>CarpenterLee</author><pubDate>Tue, 31 May 2016 07:28:00 GMT</pubDate><guid>http://www.tkk7.com/CarpenterLee/archive/2016/05/31/430716.html</guid><wfw:comment>http://www.tkk7.com/CarpenterLee/comments/430716.html</wfw:comment><comments>http://www.tkk7.com/CarpenterLee/archive/2016/05/31/430716.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/CarpenterLee/comments/commentRss/430716.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/CarpenterLee/services/trackbacks/430716.html</trackback:ping><description><![CDATA[<div class="ak6o0e8" id="cnblogs_post_body" class="cnblogs-markdown"><h1 id="introduction">Introduction</h1><p>关于<em>C++标准模板?Standard Template Library, STL)</em>的书c和资料有很多,关于<em>Java集合框架(Java Collections Framework, JCF)</em>的资料却很少Q甚臛_难找C本专门介l它的书c,q给Java学习者们带来不小的麻烦。我深深的不解其中的原因?strong>虽然JCF设计参考了STLQ但其定位不是Java版的STLQ而是要实C个精紧凑的容器框?/strong>Q对STL的介l自然不能替代对JCF的介l?/p><p>本系列文章主要从<strong>数据l构和算?/strong>层面分析JCF中List, Set, Map, Stack, Queue{典型容器,<strong>l合生动图解和源代码Q帮助读者对Java集合框架建立清晰而深入的理解</strong>。本文ƈ不特意介lJava的语aҎ,但会在需要的时候做出简z的解释?/p><h1 id="contents">Contents</h1><p>具体内容安排如下Q?/p><ol><li><a >Java Collections Framework概览</a> 对Java Collections FrameworkQ以及Java语言Ҏ做出基本介l?/li><li><a >Java ArrayList源码剖析</a> l合源码?em>ArrayList</em>q行讲解?/li><li><a >Java LinkedList源码剖析</a> l合源码?em>LinkedList</em>q行讲解?/li><li><a >Java ArrayDeque源码剖析</a> ?em>AarryDeque</em>Z讲解<em>Stack</em>?em>Queue</em>?/li><li><a >史上最清晰的红黑树讲解Q上Q?/a>?a >史上最清晰的红黑树讲解Q下Q?/a> l合源码?em>TreeSet</em>?em>TreeMap</em>q行讲解?/li><li><a >Java HashSet和HashMap源码剖析</a> l合源码?em>HashSet</em>?em>HashMap</em>q行讲解?/li><li><a >Java集合框架源码剖析QLinkedHashSet ?LinkedHashMap</a> l合源码?em>LinkedHashSet</em>?em>LinkedHashMap</em>q行讲解?/li><li><a >深入理解Java PriorityQueue</a> l合源码?em>PriorityQueue</em>q行讲解?/li><li><a >谈WeakHashMap</a> ?em>WeakHashMap</em>做出基本介绍?/li></ol><h1 id="authors">Authors</h1><table><thead><tr class="header"><th align="left">Name</th><th align="left">Weibo Id</th><th align="left">GitHub</th><th align="left">Mail</th></tr></thead><tbody><tr class="odd"><td align="left">李豪</td><td align="left"><a >@计算所的小鼠标</a></td><td align="left"><a >CarpenterLee</a></td><td align="left"><a href="mailto:hooleeucas@163.com">hooleeucas@163.com</a></td></tr></tbody></table><p><strong><br />以上所有博文均?a >博主GitHub</a>上有副本Qƈ且能保证最新版本。欢q各位博友关?/strong>?br /><br /></p></div><img src ="http://www.tkk7.com/CarpenterLee/aggbug/430716.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/CarpenterLee/" target="_blank">CarpenterLee</a> 2016-05-31 15:28 <a href="http://www.tkk7.com/CarpenterLee/archive/2016/05/31/430716.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>谈WeakHashMaphttp://www.tkk7.com/CarpenterLee/archive/2016/05/31/430712.htmlCarpenterLeeCarpenterLeeMon, 30 May 2016 23:27:00 GMThttp://www.tkk7.com/CarpenterLee/archive/2016/05/31/430712.htmlhttp://www.tkk7.com/CarpenterLee/comments/430712.htmlhttp://www.tkk7.com/CarpenterLee/archive/2016/05/31/430712.html#Feedback0http://www.tkk7.com/CarpenterLee/comments/commentRss/430712.htmlhttp://www.tkk7.com/CarpenterLee/services/trackbacks/430712.html

Java WeakHashMap 到底Weak在哪里,它真的很弱吗Q?em>WeakHashMap 的适用场景是什么,使用旉要注意些什么?弱引用和强引用对Java GC有什么不同媄响?本文给出清晰而简z的介绍?/p>

M介绍

在Java集合框架pd文章的最后,W者打介l一个特D的成员Q?em>WeakHashMapQ从名字可以看出它是某种 Map。它的特D之处在?WeakHashMap 里的entry可能会被GC自动删除Q即使程序员没有调用remove()或?code>clear()Ҏ?/p>

更直观的_当?WeakHashMap Ӟ即没有昄的添加或删除M元素Q也可能发生如下情况Q?/p>

  • 调用两次size()Ҏq回不同的|
  • 两次调用isEmpty()ҎQ第一ơ返?code>falseQ第二次q回trueQ?/li>
  • 两次调用containsKey()ҎQ第一ơ返?code>trueQ第二次q回falseQ尽两ơ用的是同一?code>keyQ?/li>
  • 两次调用get()ҎQ第一ơ返回一?code>valueQ第二次q回nullQ尽两ơ用的是同一个对象?/li>

遇到q么奇葩的现象,你是不是觉得使用者一定会疯掉Q其实不ӞWeekHashMap 的这个特点特别适用于需要缓存的场景。在~存场景下,׃内存是有限的Q不能缓存所有对象;对象~存命中可以提高pȝ效率Q但~存MISS也不会造成错误Q因为可以通过计算重新得到?/p>

要明?WeekHashMap 的工作原理,q需要引入一个概念:弱引用(WeakReferenceQ?/strong>。我们都知道Java中内存是通过GC自动理的,GC会在E序q行q程中自动判断哪些对象是可以被回收的Qƈ在合适的时机q行内存释放。GC判断某个对象是否可被回收的依据是Q?strong>是否有有效的引用指向该对?/strong>。如果没有有效引用指向该对象Q基本意味着不存在访问该对象的方式)Q那么该对象是可回收的。这里的“有效引用”q不包括弱引?/strong>。也是_虽然弱引用可以用来访问对象,但进行垃圑֛收时弱引用ƈ不会被考虑在内Q仅有弱引用指向的对象仍然会被GC回收?/p>

WeakHashMap 内部是通过弱引用来理entry的,弱引用的Ҏ对应到 WeakHashMap 上意味着什么呢Q?strong>一?code>key, value攑օ?WeakHashMap 里ƈ不能避免?code>keyDGC回收Q除非在 WeakHashMap 之外q有对该key的强引用?/p>

关于强引用,弱引用等概念以后再具体讲解,q里只需要知道Java中引用也是分U类的,q且不同U类的引用对GC的媄响不同就够了?/p>

具体实现

WeakHashMap的存储结构类gHashMapQ读者可自行参考前?/a>Q这里不再赘q?/p>

关于强弱引用的管理方式,博主会另开专题单独讲解?/p>

Weak HashSet?

如果你看q前几篇关于 Map ?Set 的讲解,一定会问:既然?WeekHashMapQ是否有 WeekHashSet 呢?{案是没?( 。不qJava Collections工具cȝZ解决ҎQ?code>Collections.newSetFromMap(Map<E,Boolean> map)Ҏ可以Q?Map包装成一?em>Set。通过如下方式可以快速得C?Weak HashSetQ?br />

不出你所料,newSetFromMap()Ҏ只是对传入的 Map做了单包装:

// Collections.newSetFromMap()用于Q何Map包装成一个Set
public static <E> Set<E> newSetFromMap(Map<E, Boolean> map) {
    return new SetFromMap<>(map);
}

private static class SetFromMap<E> extends AbstractSet<E>
    implements Set<E>, Serializable
{
    private final Map<E, Boolean> m;  // The backing map
    private transient Set<E> s;       // Its keySet
    SetFromMap(Map<E, Boolean> map) {
        if (!map.isEmpty())
            throw new IllegalArgumentException("Map is non-empty");
        m = map;
        s = map.keySet();
    }
    public void clear()               {        m.clear(); }
    public int size()                 { return m.size(); }
    public boolean isEmpty()          { return m.isEmpty(); }
    public boolean contains(Object o) { return m.containsKey(o); }
    public boolean remove(Object o)   { return m.remove(o) != null; }
    public boolean add(E e) { return m.put(e, Boolean.TRUE) == null; }
    public Iterator<E> iterator()     { return s.iterator(); }
    public Object[] toArray()         { return s.toArray(); }
    public <T> T[] toArray(T[] a)     { return s.toArray(a); }
    public String toString()          { return s.toString(); }
    public int hashCode()             { return s.hashCode(); }
    public boolean equals(Object o)   { return o == this || s.equals(o); }
    public boolean containsAll(Collection<?> c) {return s.containsAll(c);}
    public boolean removeAll(Collection<?> c)   {return s.removeAll(c);}
    public boolean retainAll(Collection<?> c)   {return s.retainAll(c);}
    // addAll is the only inherited implementation
    
}

l语

x深入Java集合框架(Java Collections Framework Internals)pd已经全部讲解完毕Q希望这几篇短的博文能够帮助各位读者对Java容器框架建立基本的理解。通过q里可以q回本系列文章目?/a>?/p>

如果对各位有哪怕些微的帮助Q博d感到非常高兴Q如果博文中有Q何的U漏和谬误,Ƣ迎各位博友指正?/p>

本文GitHub地址



CarpenterLee 2016-05-31 07:27 发表评论
]]>
Java集合框架源码剖析QLinkedHashSet ?LinkedHashMaphttp://www.tkk7.com/CarpenterLee/archive/2016/05/30/430703.htmlCarpenterLeeCarpenterLeeMon, 30 May 2016 01:19:00 GMThttp://www.tkk7.com/CarpenterLee/archive/2016/05/30/430703.htmlhttp://www.tkk7.com/CarpenterLee/comments/430703.htmlhttp://www.tkk7.com/CarpenterLee/archive/2016/05/30/430703.html#Feedback0http://www.tkk7.com/CarpenterLee/comments/commentRss/430703.htmlhttp://www.tkk7.com/CarpenterLee/services/trackbacks/430703.html

Java LinkedHashMap?em>HashMap有什么区别和联系Qؓ什?em>LinkedHashMap会有着更快的P代速度Q?em>LinkedHashSet?em>LinkedHashMap有着怎样的内在联p?本文从数据结构和法层面Q结合生动图解ؓ读者一一解答?/p>

本文github地址

M介绍

如果你已看过前面关于HashSet?em>HashMapQ以?em>TreeSet?em>TreeMap的讲解,一定能够想到本文将要讲解的LinkedHashSet?em>LinkedHashMap其实也是一回事?em>LinkedHashSet?em>LinkedHashMap在Java里也有着相同的实玎ͼ前者仅仅是对后者做了一层包装,也就是说LinkedHashSet里面有一?em>LinkedHashMapQ适配器模式)。因此本文将重点分析LinkedHashMap?/p>

LinkedHashMap实现?em>Map接口Q即允许攑օkey?code>null的元素,也允许插?code>value?code>null的元素。从名字上可以看容器?em>linked list?em>HashMap的؜合体Q也是说它同时满HashMap?em>linked list的某些特性?strong>可将LinkedHashMap看作采用linked list增强?em>HashMap?/strong>

LinkedHashMap_base.png

事实?em>LinkedHashMap?em>HashMap的直接子c,二者唯一的区别是LinkedHashMap?em>HashMap的基上,采用双向链表Qdoubly-linked listQ的形式所?code>entryq接hQ这hZ证元素的q代序跟插入顺序相?/strong>。上囄ZLinkedHashMap的结构图Q主体部分跟HashMap完全一P多了header指向双向链表的头部(是一个哑元)Q?strong>该双向链表的q代序是entry的插入顺?/strong>?/p>

除了可以保P代历序Q这U结构还有一个好处:q代LinkedHashMap时不需要像HashMap那样遍历整个tableQ而只需要直接遍?code>header指向的双向链表即?/strong>Q也是?em>LinkedHashMap的P代时间就只跟entry的个数相养I而跟table的大无兟?/p>

有两个参数可以媄?em>LinkedHashMap的性能Q初始容量(inital capacityQ和负蝲pLQload factorQ。初始容量指定了初始table的大,负蝲pL用来指定自动扩容的界倹{当entry的数量超q?code>capacity*load_factorӞ容器自动扩容ƈ重新哈希。对于插入元素较多的场景Q将初始定w讑֤可以减少重新哈希的次数?/p>

对象放入到LinkedHashMap?em>LinkedHashSet中时Q有两个Ҏ需要特别关心:hashCode()?code>equals()?strong>hashCode()Ҏ军_了对象会被放到哪?code>bucket里,当多个对象的哈希值冲H时Q?code>equals()Ҏ军_了这些对象是否是“同一个对?#8221;。所以,如果要将自定义的对象攑օ?code>LinkedHashMap?code>LinkedHashSet中,需?@Override*hashCode()?code>equals()Ҏ?/p>

通过如下方式可以得到一个跟?em>Mapq代序一LLinkedHashMapQ?br />

void foo(Map m) {
    Map copy = new LinkedHashMap(m);
    
}

Z性能原因Q?em>LinkedHashMap是非同步的(not synchronizedQ,如果需要在多线E环境用,需要程序员手动同步Q或者通过如下方式?em>LinkedHashMap包装成(wrappedQ同步的Q?/p>

Map m = Collections.synchronizedMap(new LinkedHashMap(...));

Ҏ剖析

get()

get(Object key)ҎҎ指定?code>keyD回对应的value。该Ҏ?code>HashMap.get()Ҏ的流E几乎完全一P读者可自行参考前?/a>Q这里不再赘q?/p>

put()

put(K key, V value)Ҏ是将指定?code>key, valueҎ加到map里。该Ҏ首先会对map做一ơ查找,看是否包含该元组Q如果已l包含则直接q回Q查找过E类gget()ҎQ如果没有找刎ͼ则会通过addEntry(int hash, K key, V value, int bucketIndex)Ҏ插入新的entry?/p>

注意Q这里的插入有两重含?/strong>Q?/p>

  1. ?code>table的角度看Q新?code>entry需要插入到对应?code>bucket里,当有哈希冲突Ӟ采用头插法将新的entry插入到冲H链表的头部?/li>
  2. ?code>header的角度看Q新?code>entry需要插入到双向链表的尾部?/li>

LinkedHashMap_addEntry.png

addEntry()代码如下Q?br />

上述代码中用CaddBefore()Ҏ新entry e插入到双向链表头引用header的前面,q样e成为双向链表中的最后一个元素?code>addBefore()的代码如下:

上述代码只是单修改相?code>entry的引用而已?/p>

remove()

remove(Object key)的作用是删除key值对应的entryQ该Ҏ的具体逻辑是在removeEntryForKey(Object key)里实现的?code>removeEntryForKey()Ҏ会首先找?code>key值对应的entryQ然后删除该entryQ修攚w表的相应引用Q。查找过E跟get()ҎcM?/p>

注意Q这里的删除也有两重含义Q?/p>

  1. ?code>table的角度看Q需要将?code>entry从对应的bucket里删除,如果对应的冲H链表不I,需要修改冲H链表的相应引用?/li>
  2. ?code>header的角度来看,需要将?code>entry从双向链表中删除Q同时修攚w表中前面以及后面元素的相应引用?/li>

LinkedHashMap_removeEntryForKey.png

removeEntryForKey()对应的代码如下:

// LinkedHashMap.removeEntryForKey()Q删除key值对应的entry
final Entry<K,V> removeEntryForKey(Object key) {
    
    int hash = (key == null) ? 0 : hash(key);
    int i = indexFor(hash, table.length);// hash&(table.length-1)
    Entry<K,V> prev = table[i];// 得到冲突链表
    Entry<K,V> e = prev;
    while (e != null) {// 遍历冲突链表
        Entry<K,V> next = e.next;
        Object k;
        if (e.hash == hash &&
            ((k = e.key) == key || (key != null && key.equals(k)))) {// 扑ֈ要删除的entry
            modCount++; size--;
            // 1. e从对应bucket的冲H链表中删除
            if (prev == e) table[i] = next;
            else prev.next = next;
            // 2. e从双向链表中删除
            e.before.after = e.after;
            e.after.before = e.before;
            return e;
        }
        prev = e; e = next;
    }
    return e;
}

LinkedHashSet

前面已经说过LinkedHashSet是对LinkedHashMap的简单包装,?em>LinkedHashSet的函数调用都会{换成合适的LinkedHashMapҎQ因?em>LinkedHashSet的实现非常简单,q里不再赘述?br />



CarpenterLee 2016-05-30 09:19 发表评论
]]>
史上最清晰的红黑树讲解Q下Q?/title><link>http://www.tkk7.com/CarpenterLee/archive/2016/05/25/430645.html</link><dc:creator>CarpenterLee</dc:creator><author>CarpenterLee</author><pubDate>Wed, 25 May 2016 08:48:00 GMT</pubDate><guid>http://www.tkk7.com/CarpenterLee/archive/2016/05/25/430645.html</guid><wfw:comment>http://www.tkk7.com/CarpenterLee/comments/430645.html</wfw:comment><comments>http://www.tkk7.com/CarpenterLee/archive/2016/05/25/430645.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/CarpenterLee/comments/commentRss/430645.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/CarpenterLee/services/trackbacks/430645.html</trackback:ping><description><![CDATA[<div class="ikaoka8" id="cnblogs_post_body" class="cnblogs-markdown"><p><a >本文github地址</a></p><p>上一文?a >史上最清晰的红黑树讲解Q上Q?/a>对Java <em>TreeMap</em>的插入以及插入之后的调整q程l出了详q?strong>本文接着以Java <em>TreeMap</em>ZQ从源码层面讲解U黑树的删除Q以及删除之后的调整q程</strong>。如果还没有看过上一文章,请在阅读本文之前大致览一下前文,以方便理解?/p><h2 id="L节点后">L节点后</h2><p>对于一二叉查找树Q给定节点tQ其后Q树U比大于t的最的那个元素Q可以通过如下方式扑ֈQ?/p><blockquote><ol><li>t的右子树不空Q则t的后l是其右子树中最的那个元素?/li><li>t的右孩子为空Q则t的后l是其第一个向左走的祖先?/li></ol></blockquote><p>后节点在红黑树的删除操作中会用到?/p><p><img src="http://images2015.cnblogs.com/blog/939998/201605/939998-20160525090153256-1268136762.png" alt="TreeMap_successor.png" width="800" height="379" /></p><p><em>TreeMap</em>中寻找节点后l的代码如下Q?br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; "> L节点后函数successor()</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">static</span> <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {<br />    <span style="color: #0000FF; ">if</span> (t == <span style="color: #0000FF; ">null</span>)<br />        <span style="color: #0000FF; ">return</span> <span style="color: #0000FF; ">null</span>;<br />    <span style="color: #0000FF; ">else</span> <span style="color: #0000FF; ">if</span> (t.right != <span style="color: #0000FF; ">null</span>) {<span style="color: #008000; ">//</span><span style="color: #008000; "> 1. t的右子树不空Q则t的后l是其右子树中最的那个元素</span><span style="color: #008000; "><br /></span>        Entry<K,V> p = t.right;<br />        <span style="color: #0000FF; ">while</span> (p.left != <span style="color: #0000FF; ">null</span>)<br />            p = p.left;<br />        <span style="color: #0000FF; ">return</span> p;<br />    } <span style="color: #0000FF; ">else</span> {<span style="color: #008000; ">//</span><span style="color: #008000; "> 2. t的右孩子为空Q则t的后l是其第一个向左走的祖?/span><span style="color: #008000; "><br /></span>        Entry<K,V> p = t.parent;<br />        Entry<K,V> ch = t;<br />        <span style="color: #0000FF; ">while</span> (p != <span style="color: #0000FF; ">null</span> && ch == p.right) {<br />            ch = p;<br />            p = p.parent;<br />        }<br />        <span style="color: #0000FF; ">return</span> p;<br />    }<br />}</div><div id="8s0o0is" class="sourceCode"><pre class="sourceCode java"><br /></pre></div><h2 id="remove">remove()</h2><p><code>remove(Object key)</code>的作用是删除<code>key</code>值对应的<code>entry</code>Q该Ҏ首先通过上文中提到的<code>getEntry(Object key)</code>Ҏ扑ֈ<code>key</code>值对应的<code>entry</code>Q然后调?code>deleteEntry(Entry<K,V> entry)</code>删除对应?code>entry</code>。由于删除操作会改变U黑树的l构Q有可能破坏U黑树的U束条gQ因此有可能要进行调整?/p><p><code>getEntry()</code>函数前面已经讲解q,q里重点?code>deleteEntry()</code>上,该函数删除指定的<code>entry</code>q在U黑树的U束被破坏时q行调用<code>fixAfterDeletion(Entry<K,V> x)</code>q行调整?/p><p><strong>׃U黑树是一增强版的二叉查找树Q红黑树的删除操作跟普通二叉查找树的删除操作也非常相|唯一的区别是U黑树在节点删除之后可能需要进行调?/strong>。现在考虑一|通二叉查找树的删除过E,可以单分ZU情况:</p><blockquote><ol><li>删除点p的左叛_树都为空Q或者只有一子树非I?/li><li>删除点p的左叛_树都非空?/li></ol></blockquote><p>对于上述情况1Q处理v来比较简单,直接p删除Q左叛_树都为空ӞQ或者用非空子树替代pQ只有一子树非I时Q;对于情况2Q可以用p的后lsQ树中大于x的最的那个元素Q代替pQ然后用情?删除sQ此时s一定满x?Q可以画ȝQ?/p><p>Z以上逻辑Q红黑树的节点删除函?code>deleteEntry()</code>代码如下Q?br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; "> U黑树entry删除函数deleteEntry()</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">private</span> <span style="color: #0000FF; ">void</span> deleteEntry(Entry<K,V> p) {<br />    modCount++;<br />    size--;<br />    <span style="color: #0000FF; ">if</span> (p.left != <span style="color: #0000FF; ">null</span> && p.right != <span style="color: #0000FF; ">null</span>) {<span style="color: #008000; ">//</span><span style="color: #008000; "> 2. 删除点p的左叛_树都非空?/span><span style="color: #008000; "><br /></span>        Entry<K,V> s = successor(p);<span style="color: #008000; ">//</span><span style="color: #008000; "> 后</span><span style="color: #008000; "><br /></span>        p.key = s.key;<br />        p.value = s.value;<br />        p = s;<br />    }<br />    Entry<K,V> replacement = (p.left != <span style="color: #0000FF; ">null</span> ? p.left : p.right);<br />    <span style="color: #0000FF; ">if</span> (replacement != <span style="color: #0000FF; ">null</span>) {<span style="color: #008000; ">//</span><span style="color: #008000; "> 1. 删除点p只有一子树非I?/span><span style="color: #008000; "><br /></span>        replacement.parent = p.parent;<br />        <span style="color: #0000FF; ">if</span> (p.parent == <span style="color: #0000FF; ">null</span>)<br />            root = replacement;<br />        <span style="color: #0000FF; ">else</span> <span style="color: #0000FF; ">if</span> (p == p.parent.left)<br />            p.parent.left  = replacement;<br />        <span style="color: #0000FF; ">else</span><br />            p.parent.right = replacement;<br />        p.left = p.right = p.parent = <span style="color: #0000FF; ">null</span>;<br />        <span style="color: #0000FF; ">if</span> (p.color == BLACK)<br />            fixAfterDeletion(replacement);<span style="color: #008000; ">//</span><span style="color: #008000; "> 调整</span><span style="color: #008000; "><br /></span>    } <span style="color: #0000FF; ">else</span> <span style="color: #0000FF; ">if</span> (p.parent == <span style="color: #0000FF; ">null</span>) {<br />        root = <span style="color: #0000FF; ">null</span>;<br />    } <span style="color: #0000FF; ">else</span> { <span style="color: #008000; ">//</span><span style="color: #008000; "> 1. 删除点p的左叛_树都为空</span><span style="color: #008000; "><br /></span>        <span style="color: #0000FF; ">if</span> (p.color == BLACK)<br />            fixAfterDeletion(p);<span style="color: #008000; ">//</span><span style="color: #008000; "> 调整</span><span style="color: #008000; "><br /></span>        <span style="color: #0000FF; ">if</span> (p.parent != <span style="color: #0000FF; ">null</span>) {<br />            <span style="color: #0000FF; ">if</span> (p == p.parent.left)<br />                p.parent.left = <span style="color: #0000FF; ">null</span>;<br />            <span style="color: #0000FF; ">else</span> <span style="color: #0000FF; ">if</span> (p == p.parent.right)<br />                p.parent.right = <span style="color: #0000FF; ">null</span>;<br />            p.parent = <span style="color: #0000FF; ">null</span>;<br />        }<br />    }<br />}</div><div id="igiceeg" class="sourceCode"><pre class="sourceCode java"><br /></pre></div><p>上述代码中占据大量代码行的,是用来修改父子节炚w引用关系的代码,光辑q不隄解。下面着重讲解删除后调整函数<code>fixAfterDeletion()</code>。首先请思考一下,删除了哪些点才会D调整Q?strong>只有删除ҎBLACK的时候,才会触发调整函数</strong>Q因为删除RED节点不会破坏U黑树的MU束Q而删除BLACK节点会破坏规??/p><p>跟上文中讲过?code>fixAfterInsertion()</code>函数一Pq里也要分成若干U情c记住,无论有多情况,具体的调整操作只有两U:1.改变某些节点的颜Ԍ2.Ҏ些节点进行旋转?/p><p><img src="http://images2015.cnblogs.com/blog/939998/201605/939998-20160525072940334-1483504480.png" alt="TreeMap_fixAfterDeletion.png" width="800" height="1304" /></p><p>上述图解的M思想是:情?首先转换成情?Q或者{换成情况3和情?。当Ӟ该图解ƈ不意味着调整q程一定是从情?开始。通过后箋代码我们q会发现几个有趣的规则:a).如果是由情况1之后紧接着q入的情?Q那么情?之后一定会退出@环(因ؓx为红ԌQb).一旦进入情?和情?Q一定会退出@环(因ؓx为rootQ?/p><p>删除后调整函?code>fixAfterDeletion()</code>的具体代码如下,其中用到了上文中提到?code>rotateLeft()</code>?code>rotateRight()</code>函数。通过代码我们能够看到Q情?其实是落在情?内的。情?~情?跟前四种情况是对U的Q因此图解中q没有画出后四种情况Q读者可以参考代码自行理解?br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">private</span> <span style="color: #0000FF; ">void</span> fixAfterDeletion(Entry<K,V> x) {<br />    <span style="color: #0000FF; ">while</span> (x != root && colorOf(x) == BLACK) {<br />        <span style="color: #0000FF; ">if</span> (x == leftOf(parentOf(x))) {<br />            Entry<K,V> sib = rightOf(parentOf(x));<br />            <span style="color: #0000FF; ">if</span> (colorOf(sib) == RED) {<br />                setColor(sib, BLACK);                   <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况1</span><span style="color: #008000; "><br /></span>                setColor(parentOf(x), RED);             <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况1</span><span style="color: #008000; "><br /></span>                rotateLeft(parentOf(x));                <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况1</span><span style="color: #008000; "><br /></span>                sib = rightOf(parentOf(x));             <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况1</span><span style="color: #008000; "><br /></span>            }<br />            <span style="color: #0000FF; ">if</span> (colorOf(leftOf(sib))  == BLACK &&<br />                colorOf(rightOf(sib)) == BLACK) {<br />                setColor(sib, RED);                     <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况2</span><span style="color: #008000; "><br /></span>                x = parentOf(x);                        <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况2</span><span style="color: #008000; "><br /></span>            } <span style="color: #0000FF; ">else</span> {<br />                <span style="color: #0000FF; ">if</span> (colorOf(rightOf(sib)) == BLACK) {<br />                    setColor(leftOf(sib), BLACK);       <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况3</span><span style="color: #008000; "><br /></span>                    setColor(sib, RED);                 <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况3</span><span style="color: #008000; "><br /></span>                    rotateRight(sib);                   <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况3</span><span style="color: #008000; "><br /></span>                    sib = rightOf(parentOf(x));         <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况3</span><span style="color: #008000; "><br /></span>                }<br />                setColor(sib, colorOf(parentOf(x)));    <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况4</span><span style="color: #008000; "><br /></span>                setColor(parentOf(x), BLACK);           <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况4</span><span style="color: #008000; "><br /></span>                setColor(rightOf(sib), BLACK);          <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况4</span><span style="color: #008000; "><br /></span>                rotateLeft(parentOf(x));                <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况4</span><span style="color: #008000; "><br /></span>                x = root;                               <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况4</span><span style="color: #008000; "><br /></span>            }<br />        } <span style="color: #0000FF; ">else</span> { <span style="color: #008000; ">//</span><span style="color: #008000; "> 跟前四种情况对称</span><span style="color: #008000; "><br /></span>            Entry<K,V> sib = leftOf(parentOf(x));<br />            <span style="color: #0000FF; ">if</span> (colorOf(sib) == RED) {<br />                setColor(sib, BLACK);                   <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况5</span><span style="color: #008000; "><br /></span>                setColor(parentOf(x), RED);             <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况5</span><span style="color: #008000; "><br /></span>                rotateRight(parentOf(x));               <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况5</span><span style="color: #008000; "><br /></span>                sib = leftOf(parentOf(x));              <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况5</span><span style="color: #008000; "><br /></span>            }<br />            <span style="color: #0000FF; ">if</span> (colorOf(rightOf(sib)) == BLACK &&<br />                colorOf(leftOf(sib)) == BLACK) {<br />                setColor(sib, RED);                     <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况6</span><span style="color: #008000; "><br /></span>                x = parentOf(x);                        <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况6</span><span style="color: #008000; "><br /></span>            } <span style="color: #0000FF; ">else</span> {<br />                <span style="color: #0000FF; ">if</span> (colorOf(leftOf(sib)) == BLACK) {<br />                    setColor(rightOf(sib), BLACK);      <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况7</span><span style="color: #008000; "><br /></span>                    setColor(sib, RED);                 <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况7</span><span style="color: #008000; "><br /></span>                    rotateLeft(sib);                    <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况7</span><span style="color: #008000; "><br /></span>                    sib = leftOf(parentOf(x));          <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况7</span><span style="color: #008000; "><br /></span>                }<br />                setColor(sib, colorOf(parentOf(x)));    <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况8</span><span style="color: #008000; "><br /></span>                setColor(parentOf(x), BLACK);           <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况8</span><span style="color: #008000; "><br /></span>                setColor(leftOf(sib), BLACK);           <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况8</span><span style="color: #008000; "><br /></span>                rotateRight(parentOf(x));               <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况8</span><span style="color: #008000; "><br /></span>                x = root;                               <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况8</span><span style="color: #008000; "><br /></span>            }<br />        }<br />    }<br />    setColor(x, BLACK);<br />}</div><div id="o8wwqia" class="sourceCode"><pre class="sourceCode java"><br /></pre></div><h1 id="treeset">TreeSet</h1><p>前面已经说过<code>TreeSet</code>是对<code>TeeMap</code>的简单包装,?code>TreeSet</code>的函数调用都会{换成合适的<code>TeeMap</code>ҎQ因?code>TreeSet</code>的实现非常简单。这里不再赘q?br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; "> TreeSet是对TreeMap的简单包?/span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">public</span> <span style="color: #0000FF; ">class</span> TreeSet<E> <span style="color: #0000FF; ">extends</span> AbstractSet<E><br />    <span style="color: #0000FF; ">implements</span> NavigableSet<E>, Cloneable, java.io.Serializable<br />{<br />    <img src="http://www.tkk7.com/Images/dot.gif" alt="" /><img src="http://www.tkk7.com/Images/dot.gif" alt="" /><br />    <span style="color: #0000FF; ">private</span> <span style="color: #0000FF; ">transient</span> NavigableMap<E,Object> m;<br />    <span style="color: #008000; ">//</span><span style="color: #008000; "> Dummy value to associate with an Object in the backing Map</span><span style="color: #008000; "><br /></span>    <span style="color: #0000FF; ">private</span> <span style="color: #0000FF; ">static</span> <span style="color: #0000FF; ">final</span> Object PRESENT = <span style="color: #0000FF; ">new</span> Object();<br />    <span style="color: #0000FF; ">public</span> TreeSet() {<br />        <span style="color: #0000FF; ">this</span>.m = <span style="color: #0000FF; ">new</span> TreeMap<E,Object>();<span style="color: #008000; ">//</span><span style="color: #008000; "> TreeSet里面有一个TreeMap</span><span style="color: #008000; "><br /></span>    }<br />    <img src="http://www.tkk7.com/Images/dot.gif" alt="" /><img src="http://www.tkk7.com/Images/dot.gif" alt="" /><br />    <span style="color: #0000FF; ">public</span> <span style="color: #0000FF; ">boolean</span> add(E e) {<br />        <span style="color: #0000FF; ">return</span> m.put(e, PRESENT)==<span style="color: #0000FF; ">null</span>;<br />    }<br />    <img src="http://www.tkk7.com/Images/dot.gif" alt="" /><img src="http://www.tkk7.com/Images/dot.gif" alt="" /><br />}</div><div id="eq6es0s" class="sourceCode"><pre class="sourceCode java"><br /></pre></div></div><img src ="http://www.tkk7.com/CarpenterLee/aggbug/430645.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/CarpenterLee/" target="_blank">CarpenterLee</a> 2016-05-25 16:48 <a href="http://www.tkk7.com/CarpenterLee/archive/2016/05/25/430645.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>史上最清晰的红黑树讲解Q上Q?/title><link>http://www.tkk7.com/CarpenterLee/archive/2016/05/18/430564.html</link><dc:creator>CarpenterLee</dc:creator><author>CarpenterLee</author><pubDate>Tue, 17 May 2016 23:57:00 GMT</pubDate><guid>http://www.tkk7.com/CarpenterLee/archive/2016/05/18/430564.html</guid><wfw:comment>http://www.tkk7.com/CarpenterLee/comments/430564.html</wfw:comment><comments>http://www.tkk7.com/CarpenterLee/archive/2016/05/18/430564.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.tkk7.com/CarpenterLee/comments/commentRss/430564.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/CarpenterLee/services/trackbacks/430564.html</trackback:ping><description><![CDATA[<div class="8wm0000" id="cnblogs_post_body" class="cnblogs-markdown"><p><a >本文github地址</a></p><p>本文以Java TreeMapZQ从源代码层面,l合详细的图解,剥茧抽丝地讲解红黑树QRed-Black treeQ的插入Q删除以及由此生的调整q程?/p><h1 id="M介绍">M介绍</h1><p>Java <em>TreeMap</em>实现?em>SortedMap</em>接口Q也是说会按照<code>key</code>的大顺序对<em>Map</em>中的元素q行排序Q?code>key</code>大小的评判可以通过其本w的自然序Qnatural orderingQ,也可以通过构造时传入的比较器QComparatorQ?/p><p><strong><em>TreeMap</em>底层通过U黑树(Red-Black treeQ实?/strong>Q也意味着<code>containsKey()</code>, <code>get()</code>, <code>put()</code>, <code>remove()</code>都有着<code>log(n)</code>的时间复杂度。其具体法实现参照了《算法导论》?/p><p><img src="http://images2015.cnblogs.com/blog/939998/201605/939998-20160517211933779-124491145.png" alt="TreeMap_base.png" /></p><p>Z性能原因Q?em>TreeMap</em>是非同步的(not synchronizedQ,如果需要在多线E环境用,需要程序员手动同步Q或者通过如下方式?em>TreeMap</em>包装成(wrappedQ同步的Q?/p><p><code>SortedMap m = Collections.synchronizedSortedMap(new TreeMap(...));</code></p><p><strong>U黑树是一U近似^衡的二叉查找树,它能够确保Q何一个节点的左右子树的高度差不会过二者中较低那个的一?/strong>。具体来_U黑树是满如下条g的二叉查找树Qbinary search treeQ:</p><ol><li>每个节点要么是红Ԍ要么是黑艌Ӏ?/li><li>根节点必L黑色</li><li>U色节点不能q箋Q也xQ红色节点的孩子和父亲都不能是红Ԍ?/li><li>对于每个节点Q从该点?code>null</code>Q树Q的M路径Q都含有相同个数的黑色节炏V?/li></ol><p>在树的结构发生改变时Q插入或者删除操作)Q往往会破坏上q条?或条?Q需要通过调整使得查找树重新满红黑树的条件?/p><h1 id="预备知识">预备知识</h1><p>前文说到当查找树的结构发生改变时Q红黑树的条件可能被破坏Q需要通过调整使得查找树重新满红黑树的条件。调整可以分Zc:一cL颜色调整Q即改变某个节点的颜Ԍ另一cLl构调整Q集改变索树的结构关pR结构调整过E包含两个基本操作:<strong>左旋QRotate LeftQ,xQRotateRightQ?/strong>?/p><h2 id="左旋">左旋</h2><p>左旋的过E是?code>x</code>的右子树l?code>x</code>逆时针旋转,使得<code>x</code>的右子树成ؓ<code>x</code>的父Ԍ同时修改相关节点的引用。旋转之后,二叉查找树的属性仍然满?/p><p><img src="http://images2015.cnblogs.com/blog/939998/201605/939998-20160517212009529-1958413310.png" alt="TreeMap_rotateLeft.png" /></p><p><em>TreeMap</em>中左旋代码如下:<br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">Rotate Left</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">private</span> <span style="color: #0000FF; ">void</span> rotateLeft(Entry<K,V> p) {<br />    <span style="color: #0000FF; ">if</span> (p != <span style="color: #0000FF; ">null</span>) {<br />        Entry<K,V> r = p.right;<br />        p.right = r.left;<br />        <span style="color: #0000FF; ">if</span> (r.left != <span style="color: #0000FF; ">null</span>)<br />            r.left.parent = p;<br />        r.parent = p.parent;<br />        <span style="color: #0000FF; ">if</span> (p.parent == <span style="color: #0000FF; ">null</span>)<br />            root = r;<br />        <span style="color: #0000FF; ">else</span> <span style="color: #0000FF; ">if</span> (p.parent.left == p)<br />            p.parent.left = r;<br />        <span style="color: #0000FF; ">else</span><br />            p.parent.right = r;<br />        r.left = p;<br />        p.parent = r;<br />    }<br />}</div><div id="k0sg08k" class="sourceCode"><pre class="sourceCode java"><br /></pre></div><h2 id="x">x</h2><p>x的过E是?code>x</code>的左子树l?code>x</code>时针旋转,使得<code>x</code>的左子树成ؓ<code>x</code>的父Ԍ同时修改相关节点的引用。旋转之后,二叉查找树的属性仍然满?/p><p><img src="http://images2015.cnblogs.com/blog/939998/201605/939998-20160517212020498-954534792.png" alt="TreeMap_rotateRight.png" /></p><p><em>TreeMap</em>中右旋代码如下:<br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">Rotate Right</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">private</span> <span style="color: #0000FF; ">void</span> rotateRight(Entry<K,V> p) {<br />    <span style="color: #0000FF; ">if</span> (p != <span style="color: #0000FF; ">null</span>) {<br />        Entry<K,V> l = p.left;<br />        p.left = l.right;<br />        <span style="color: #0000FF; ">if</span> (l.right != <span style="color: #0000FF; ">null</span>) l.right.parent = p;<br />        l.parent = p.parent;<br />        <span style="color: #0000FF; ">if</span> (p.parent == <span style="color: #0000FF; ">null</span>)<br />            root = l;<br />        <span style="color: #0000FF; ">else</span> <span style="color: #0000FF; ">if</span> (p.parent.right == p)<br />            p.parent.right = l;<br />        <span style="color: #0000FF; ">else</span> p.parent.left = l;<br />        l.right = p;<br />        p.parent = l;<br />    }<br />}</div><div id="8qci00e" class="sourceCode"><pre class="sourceCode java"><br /></pre></div><h1 id="Ҏ剖析">Ҏ剖析</h1><h2 id="get">get()</h2><p><code>get(Object key)</code>ҎҎ指定?code>key</code>D回对应的<code>value</code>Q该Ҏ调用?code>getEntry(Object key)</code>得到相应?code>entry</code>Q然后返?code>entry.value</code>。因?code>getEntry()</code>是算法的核心。算法思想是根?code>key</code>的自焉序(或者比较器序Q对二叉查找树进行查找,直到扑ֈ满<code>k.compareTo(p.key) == 0</code>?code>entry</code>?/p><p><img src="http://images2015.cnblogs.com/blog/939998/201605/939998-20160517211944357-1810109113.png" alt="TreeMap_getEntry.png" /></p><p>具体代码如下Q?br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">getEntry()Ҏ</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">final</span> Entry<K,V> getEntry(Object key) {<br />    <img src="http://www.tkk7.com/Images/dot.gif" alt="" /><img src="http://www.tkk7.com/Images/dot.gif" alt="" /><br />    <span style="color: #0000FF; ">if</span> (key == <span style="color: #0000FF; ">null</span>)<span style="color: #008000; ">//</span><span style="color: #008000; ">不允许keygؓnull</span><span style="color: #008000; "><br /></span>        <span style="color: #0000FF; ">throw</span> <span style="color: #0000FF; ">new</span> NullPointerException();<br />    Comparable<? <span style="color: #0000FF; ">super</span> K> k = (Comparable<? <span style="color: #0000FF; ">super</span> K>) key;<span style="color: #008000; ">//</span><span style="color: #008000; ">使用元素的自焉?/span><span style="color: #008000; "><br /></span>    Entry<K,V> p = root;<br />    <span style="color: #0000FF; ">while</span> (p != <span style="color: #0000FF; ">null</span>) {<br />        <span style="color: #0000FF; ">int</span> cmp = k.compareTo(p.key);<br />        <span style="color: #0000FF; ">if</span> (cmp < 0)<span style="color: #008000; ">//</span><span style="color: #008000; ">向左?/span><span style="color: #008000; "><br /></span>            p = p.left;<br />        <span style="color: #0000FF; ">else</span> <span style="color: #0000FF; ">if</span> (cmp > 0)<span style="color: #008000; ">//</span><span style="color: #008000; ">向右?/span><span style="color: #008000; "><br /></span>            p = p.right;<br />        <span style="color: #0000FF; ">else</span><br />            <span style="color: #0000FF; ">return</span> p;<br />    }<br />    <span style="color: #0000FF; ">return</span> <span style="color: #0000FF; ">null</span>;<br />}</div><div id="euuoig8" class="sourceCode"><pre class="sourceCode java"><br /></pre></div><h2 id="put">put()</h2><p><code>put(K key, V value)</code>Ҏ是将指定?code>key</code>, <code>value</code>Ҏ加到<code>map</code>里。该Ҏ首先会对<code>map</code>做一ơ查找,看是否包含该元组Q如果已l包含则直接q回Q查找过E类g<code>getEntry()</code>ҎQ如果没有找到则会在U黑树中插入新的<code>entry</code>Q如果插入之后破坏了U黑树的U束Q还需要进行调_旋{Q改变某些节点的颜色Q?br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #0000FF; ">public</span> V put(K key, V value) {<br />    <img src="http://www.tkk7.com/Images/dot.gif" alt="" /><img src="http://www.tkk7.com/Images/dot.gif" alt="" /><br />    <span style="color: #0000FF; ">int</span> cmp;<br />    Entry<K,V> parent;<br />    <span style="color: #0000FF; ">if</span> (key == <span style="color: #0000FF; ">null</span>)<br />        <span style="color: #0000FF; ">throw</span> <span style="color: #0000FF; ">new</span> NullPointerException();<br />    Comparable<? <span style="color: #0000FF; ">super</span> K> k = (Comparable<? <span style="color: #0000FF; ">super</span> K>) key;<span style="color: #008000; ">//</span><span style="color: #008000; ">使用元素的自焉?/span><span style="color: #008000; "><br /></span>    <span style="color: #0000FF; ">do</span> {<br />        parent = t;<br />        cmp = k.compareTo(t.key);<br />        <span style="color: #0000FF; ">if</span> (cmp < 0) t = t.left;<span style="color: #008000; ">//</span><span style="color: #008000; ">向左?/span><span style="color: #008000; "><br /></span>        <span style="color: #0000FF; ">else</span> <span style="color: #0000FF; ">if</span> (cmp > 0) t = t.right;<span style="color: #008000; ">//</span><span style="color: #008000; ">向右?/span><span style="color: #008000; "><br /></span>        <span style="color: #0000FF; ">else</span> <span style="color: #0000FF; ">return</span> t.setValue(value);<br />    } <span style="color: #0000FF; ">while</span> (t != <span style="color: #0000FF; ">null</span>);<br />    Entry<K,V> e = <span style="color: #0000FF; ">new</span> Entry<>(key, value, parent);<span style="color: #008000; ">//</span><span style="color: #008000; ">创徏q插入新的entry</span><span style="color: #008000; "><br /></span>    <span style="color: #0000FF; ">if</span> (cmp < 0) parent.left = e;<br />    <span style="color: #0000FF; ">else</span> parent.right = e;<br />    fixAfterInsertion(e);<span style="color: #008000; ">//</span><span style="color: #008000; ">调整</span><span style="color: #008000; "><br /></span>    size++;<br />    <span style="color: #0000FF; ">return</span> <span style="color: #0000FF; ">null</span>;<br />}</div><div id="y0cg8iw" class="sourceCode"><pre class="sourceCode java"><br /></pre></div><p>上述代码的插入部分ƈ不难理解Q首先在U黑树上扑ֈ合适的位置Q然后创建新?code>entry</code>q插入(当然Q新插入的节点一定是树的叶子Q。难Ҏ调整函数<code>fixAfterInsertion()</code>Q前面已l说q,调整往往需?.改变某些节点的颜Ԍ2.Ҏ些节点进行旋转?/p><p><img src="http://images2015.cnblogs.com/blog/939998/201605/939998-20160517212000341-1761362961.png" alt="TreeMap_put.png" /></p><p>调整函数<code>fixAfterInsertion()</code>的具体代码如下,其中用到了上文中提到?code>rotateLeft()</code>?code>rotateRight()</code>函数。通过代码我们能够看到Q情?其实是落在情?内的。情?~情?跟前三种情况是对U的Q因此图解中q没有画出后三种情况Q读者可以参考代码自行理解?br /></p><div style="background-color:#eeeeee;font-size:13px;border:1px solid #CCCCCC;padding-right: 5px;padding-bottom: 4px;padding-left: 4px;padding-top: 4px;width: 98%;word-break:break-all"><!--<br /><br />Code highlighting produced by Actipro CodeHighlighter (freeware)<br />http://www.CodeHighlighter.com/<br /><br />--><span style="color: #008000; ">//</span><span style="color: #008000; ">U黑树调整函数fixAfterInsertion()</span><span style="color: #008000; "><br /></span><span style="color: #0000FF; ">private</span> <span style="color: #0000FF; ">void</span> fixAfterInsertion(Entry<K,V> x) {<br />    x.color = RED;<br />    <span style="color: #0000FF; ">while</span> (x != <span style="color: #0000FF; ">null</span> && x != root && x.parent.color == RED) {<br />        <span style="color: #0000FF; ">if</span> (parentOf(x) == leftOf(parentOf(parentOf(x)))) {<br />            Entry<K,V> y = rightOf(parentOf(parentOf(x)));<br />            <span style="color: #0000FF; ">if</span> (colorOf(y) == RED) {<span style="color: #008000; font-family: 'Courier New', sans-serif; font-size: 12px; line-height: 18px; white-space: pre-wrap; background-color: #f5f5f5;">//如果y为nullQ则视ؓBLACK</span><br />                setColor(parentOf(x), BLACK);                         <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况1</span><span style="color: #008000; "><br /></span>                setColor(y, BLACK);                                        <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况1</span><span style="color: #008000; "><br /></span>                setColor(parentOf(parentOf(x)), RED);             <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况1</span><span style="color: #008000; "><br /></span>                x = parentOf(parentOf(x));                              <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况1</span><span style="color: #008000; "><br /></span>            } <span style="color: #0000FF; ">else</span> {<br />                <span style="color: #0000FF; ">if</span> (x == rightOf(parentOf(x))) {<br />                    x = parentOf(x);                                         <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况2</span><span style="color: #008000; "><br /></span>                    rotateLeft(x);                                              <span style="color: #008000;">//</span><span style="color: #008000; "> 情况2</span><span style="color: #008000; "><br /></span>                }<br />                setColor(parentOf(x), BLACK);                          <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况3</span><span style="color: #008000; "><br /></span>                setColor(parentOf(parentOf(x)), RED);              <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况3</span><span style="color: #008000; "><br /></span>                rotateRight(parentOf(parentOf(x)));                  <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况3</span><span style="color: #008000; "><br /></span>            }<br />        } <span style="color: #0000FF; ">else</span> {<br />            Entry<K,V> y = leftOf(parentOf(parentOf(x)));<br />            <span style="color: #0000FF; ">if</span> (colorOf(y) == RED) {<br />                setColor(parentOf(x), BLACK);                          <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况4</span><span style="color: #008000; "><br /></span>                setColor(y, BLACK);                                         <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况4</span><span style="color: #008000; "><br /></span>                setColor(parentOf(parentOf(x)), RED);              <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况4</span><span style="color: #008000; "><br /></span>                x = parentOf(parentOf(x));                              <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况4</span><span style="color: #008000; "><br /></span>            } <span style="color: #0000FF; ">else</span> {<br />                <span style="color: #0000FF; ">if</span> (x == leftOf(parentOf(x))) {<br />                    x = parentOf(x);                                         <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况5</span><span style="color: #008000; "><br /></span>                    rotateRight(x);                                            <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况5</span><span style="color: #008000; "><br /></span>                }<br />                setColor(parentOf(x), BLACK);                         <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况6</span><span style="color: #008000; "><br /></span>                setColor(parentOf(parentOf(x)), RED);             <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况6</span><span style="color: #008000; "><br /></span>                rotateLeft(parentOf(parentOf(x)));                   <span style="color: #008000; ">//</span><span style="color: #008000; "> 情况6</span><span style="color: #008000; "><br /></span>            }<br />        }<br />    }<br />    root.color = BLACK;<br />}</div><div id="8qac08a" class="sourceCode"><pre class="sourceCode java"><br /></pre></div><h2 id="remove">remove()</h2><p><code>remove(Object key)</code>的作用是删除<code>key</code>值对应的<code>entry</code>Q该Ҏ首先通过上文中提到的<code>getEntry(Object key)</code>Ҏ扑ֈ<code>key</code>值对应的<code>entry</code>Q然后调?code>deleteEntry(Entry<K,V> entry)</code>删除对应?code>entry</code>。由于删除操作会改变U黑树的l构Q有可能破坏U黑树的U束Q因此有可能要进行调整?/p><p><strong>有关<code>remove()</code>的具体讲解将攑ֈ下一博文当中,敬请期待Q?/strong></p></div><img src ="http://www.tkk7.com/CarpenterLee/aggbug/430564.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/CarpenterLee/" target="_blank">CarpenterLee</a> 2016-05-18 07:57 <a href="http://www.tkk7.com/CarpenterLee/archive/2016/05/18/430564.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java PriorityQueue源码剖析http://www.tkk7.com/CarpenterLee/archive/2016/05/12/430473.htmlCarpenterLeeCarpenterLeeThu, 12 May 2016 13:22:00 GMThttp://www.tkk7.com/CarpenterLee/archive/2016/05/12/430473.htmlhttp://www.tkk7.com/CarpenterLee/comments/430473.htmlhttp://www.tkk7.com/CarpenterLee/archive/2016/05/12/430473.html#Feedback2http://www.tkk7.com/CarpenterLee/comments/commentRss/430473.htmlhttp://www.tkk7.com/CarpenterLee/services/trackbacks/430473.html阅读全文

CarpenterLee 2016-05-12 21:22 发表评论
]]>
Z么你的博客不够火Q?/title><link>http://www.tkk7.com/CarpenterLee/archive/2016/05/11/430433.html</link><dc:creator>CarpenterLee</dc:creator><author>CarpenterLee</author><pubDate>Wed, 11 May 2016 01:02:00 GMT</pubDate><guid>http://www.tkk7.com/CarpenterLee/archive/2016/05/11/430433.html</guid><wfw:comment>http://www.tkk7.com/CarpenterLee/comments/430433.html</wfw:comment><comments>http://www.tkk7.com/CarpenterLee/archive/2016/05/11/430433.html#Feedback</comments><slash:comments>8</slash:comments><wfw:commentRss>http://www.tkk7.com/CarpenterLee/comments/commentRss/430433.html</wfw:commentRss><trackback:ping>http://www.tkk7.com/CarpenterLee/services/trackbacks/430433.html</trackback:ping><description><![CDATA[<h1 class="postTitle"><br /> </h1> <div class="u8myucw" id="cnblogs_post_body" class="cnblogs-markdown"> <h1 id="cnblog首页博客热度分析">CNBlog首页博客热度分析</h1> <p><a >本文github地址</a></p> <h1 id="前言">前言</h1> <p>每个博客园的园友或许都会有这U经历:自己辛辛苦苦Q认认真真的写了博客,然后满心Ƣ喜的发C博客园首,当你以ؓ大功告成坐等点击量暴表的时候,却发现自q博文Ҏ无h问|。那是何等的痛?(</p> <p>不要再自我怀疑,不要再自怨自艾,博客不火Q不一定是博文内容不够严}深入Q也不一定是你能力不I而可能仅仅是因ؓ你选择了错误的发表时机?/p> <p>本文Z博客园近3个月4000首博文,q用大数据分析,机器学习Q文本挖掘等先进技术,<strong>深入而细致的剖析军_博客热度的若q因素,让您从此也能写出_湛的技术博客,成ؓ博客园的技术达人(做梦到此l束...Q?/strong></p> <h1 id="技术实?>技术实?/h1> <p>l分析,博客园首늽늻构比较简z,通过爬虫抓取<a >http://www.cnblogs.com/sitehome/p/your_page_num</a> q接下的内容Q即可获取所有首博文。本文采用的?a >jsoup</a>q个Java HTML Parserq行的网|取。博客园늠只支持到200Q每?0,也就是最多能够抓?000首博客。对数据q行清洗后存储到文gQ供下一步分析?/p> <p>׃数据量ƈ不大Q分析数据采用的是excel表格。不要觉得lowQ用表格来处理小规模数据Q效果不亚于数据库?/p> <h1 id="分析l果">分析l果</h1> <h2 id="博友们一天之内喜Ƣ什么时候发博客">博友们一天之内喜Ƣ什么时候发博客Q?/h2> <h2 id="哪个旉D发的博客更Ҏ?>哪个旉D发的博客更Ҏ火?</h2> <p><img src="http://images2015.cnblogs.com/blog/939998/201605/939998-20160510224746390-1420868655.png" alt="HotOfHours.png" /></p> <p>我们对一天中不同旉D发表的博客q行l计Q然后计每个小时内的博客发表量Q以及当前这个小时每博客的q_热度。这里的热度是用来衡量一博文受Ƣ迎E度的综合指标,计算公式为:</p> <p><code>hot=(recommend*10+comment*5+view)</code></p> <p>为避免离点Q当hotDq?600时则?600处理。上图中标注的热度(U色U)为对文章热度求^均之后,然后做归一?code>(avg_hot/800*100%)</code>之后的结果?/p> <p>从上囑֏以明昄出,<strong>一天之内有三个旉D大家比较爱发博?/strong>Q分别是10:00左右Q?6:00左右?2:00左右Q这分别对应的是上午上班旉Q下午上班时_和晚上加班时间?strong>一天内也有三个旉D大家不怎么爱发博客</strong>Q分别是1:00~7:00Q?2:00左右Q?9:00左右Q分别对应大家的睡觉旉Q午饭时间和下班旉?/p> <p>什么时候发的博客更Ҏ火呢Q抛开凌晨那段旉不提Q因为博客量太少Q,上图可以看出Q早?:00左右发的博客热度最高,中午12:00左右和晚?2:00左右也是个热度小高峰?/p> <p>Ҏ上图中的蓝线和红U,我们发现博客发表高峰和访问高峎ͼ热度评估主要Z讉K量,所以热度表CZ讉K量的势Qƈ不L成比例。具体表现如下:</p> <blockquote> <ol> <li>早上8:00是一天中讉K量最高的时候,但博客发表ƈ不是很多Q上班\上大家刷刷博客?Q?/li> <li>上午10:00左右是博客发表的高峰Q但讉K量却呈下降趋势(忙着写自q博客而忘记看别h的博客)</li> <li>中午12:00左右讉K量很高,但博客发表量却出奇的低(吃饭的时候不写博客,倒是可以手机刷刷博客Q?/li> </ol> </blockquote> <h2 id="大家一周之内喜Ƣ在哪一天发博客">大家一周之内喜Ƣ在哪一天发博客Q?/h2> <h2 id="一周之内哪一天发的博客更Ҏ?>一周之内哪一天发的博客更Ҏ火?</h2> <p><img src="http://images2015.cnblogs.com/blog/939998/201605/939998-20160510224753093-1106735401.png" alt="HotOfDays.png" /></p> <p>我们对一周中不同天发表的博客q行l计Q然后计每天的博客发表量,以及当天每篇博客的^均热度?/p> <p>通过上图可以看出Q?/p> <blockquote> <ol> <li>星期一和星期二是博客发表的热潮Q上班前两天不但工作U极Q写博客也很U极Q?/li> <li>之后一直下降,到周六达到最低谷Q终于盼来周末,谁还写博客!Q?/li> <li>博客热度跟发表量基本dQ可见工作日大家不但工作热情高,写博客和d客的热情都不低?/li> <li>C周末Q写博客的h,看博客的人更!</li> <li>周四博客阅读量出C回升Q你可以帮忙x是ؓ什么?/li> </ol> </blockquote> <p>上图意味着Q?strong>周末q是老老实实的出去玩吧Q即使写了博客也不会有h看的</strong>。特别是周六Q千万不要在周六发表技术博客,切记切记Q?/p> <h1 id="ȝ">ȝ</h1> <p>l过以上分析Q我们得出结论:Z避免吃力不讨好的情况Q发表博客一定要认准时机?/p> <ol> <li><strong>博客惌火,׃能睡懒觉Q因Z要在8:00钟左叛_表博客?/strong></li> <li><strong>更不能吃午饭Q因Zq要?2:00左右发表博客?/strong></li> <li>当然Qؓ了犒劳以下忙一周的你,<strong>周末切记不要苦逼的写博客,因ؓ即写得再认真也不会有h看?/strong></li> <li><strong>周一Q周二以及周四,才是您发表博客的黄道吉日?/strong></li> </ol> <p>以上四项基本原则Q一定要牢记于心Q切C要轻易违背。否则没有点击量Q你的博客还不如写道日记本里?/p> <h1 id="未来的工?>未来的工?/h1> <p>博客热度不仅跟发表时间有养I当然也跟博客内容Q以及博ȝ个h影响力等诸多因素相关。希望各位博友能够加入更多分析?/p> <p><strong>本文用到的所有代码和数据Q都已经攑ֈ?a >博主github</a>上,Ƣ迎各位博友切磋?/strong></p> </div> <img src ="http://www.tkk7.com/CarpenterLee/aggbug/430433.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.tkk7.com/CarpenterLee/" target="_blank">CarpenterLee</a> 2016-05-11 09:02 <a href="http://www.tkk7.com/CarpenterLee/archive/2016/05/11/430433.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java ArrayDeque源码剖析http://www.tkk7.com/CarpenterLee/archive/2016/05/07/430396.htmlCarpenterLeeCarpenterLeeSat, 07 May 2016 10:30:00 GMThttp://www.tkk7.com/CarpenterLee/archive/2016/05/07/430396.htmlhttp://www.tkk7.com/CarpenterLee/comments/430396.htmlhttp://www.tkk7.com/CarpenterLee/archive/2016/05/07/430396.html#Feedback2http://www.tkk7.com/CarpenterLee/comments/commentRss/430396.htmlhttp://www.tkk7.com/CarpenterLee/services/trackbacks/430396.html

ArrayDeque

本文github地址

前言

Java里有一个叫?em>Stack的类Q却没有叫做Queue的类Q它是个接口名字Q。当需要用栈ӞJava已不推荐使用StackQ而是推荐使用更高效的ArrayDequeQ既?em>Queue只是一个接口,当需要用队列时也就首?em>ArrayDeque了(ơ选是LinkedListQ?/p>

M介绍

要讲栈和队列Q首先要?em>Deque接口?em>Deque的含义是“double ended queue”Q即双端队列Q它既可以当作栈使用Q也可以当作队列使用。下表列ZDeque?em>Queue相对应的接口Q?/p>
Queue MethodEquivalent Deque Method说明
add(e) addLast(e) 向队插入元素,p|则抛出异?/td>
offer(e) offerLast(e) 向队插入元素,p|则返?code>false
remove() removeFirst() 获取q删除队首元素,p|则抛出异?/td>
poll() pollFirst() 获取q删除队首元素,p|则返?code>null
element() getFirst() 获取但不删除队首元素Q失败则抛出异常
peek() peekFirst() 获取但不删除队首元素Q失败则q回null

下表列出?em>Deque?em>Stack对应的接口:

Stack MethodEquivalent Deque Method说明
push(e) addFirst(e) 向栈插入元素,p|则抛出异?/td>
?/td> offerFirst(e) 向栈插入元素,p|则返?code>false
pop() removeFirst() 获取q删除栈元素,p|则抛出异?/td>
?/td> pollFirst() 获取q删除栈元素,p|则返?code>null
peek() peekFirst() 获取但不删除栈顶元素Q失败则抛出异常
?/td> peekFirst() 获取但不删除栈顶元素Q失败则q回null

上面两个表共定义?em>Deque?2个接口。添加,删除Q取值都有两套接口,它们功能相同Q区别是对失败情늚处理不同?strong>一套接口遇到失败就会抛出异常,另一套遇到失败会q回Ҏ|false?code>nullQ?/strong>。除非某U实现对定w有限Ӟ大多数情况下Q添加操作是不会p|的?strong>虽然Deque的接口有12个之多,但无非就是对容器的两端进行操作,或添加,或删除,或查?/strong>。明白了q一点讲解v来就会非常简单?/p>

ArrayDeque?em>LinkedList?em>Deque的两个通用实现Q由于官Ҏ推荐使用AarryDeque用作栈和队列Q加之上一已l讲解过LinkedListQ本文将着重讲?em>ArrayDeque的具体实现?/p>

从名字可以看?em>ArrayDeque底层通过数组实现Qؓ了满_以同时在数组两端插入或删除元素的需求,该数l还必须是@环的Q即循环数组Qcircular arrayQ?/strong>Q也是说数l的M一炚w可能被看作vҎ者终炏V?em>ArrayDeque是非U程安全的(not thread-safeQ,当多个线E同时用的时候,需要程序员手动同步Q另外,该容器不允许攑օnull元素?/p>

ArrayDeque_base.png

上图中我们看刎ͼhead指向首端W一个有效元素,tail指向W一个可以插入元素的IZ。因为是循环数组Q所?code>head不一定ȝ?Q?code>tail也不一定L?code>head大?/p>

Ҏ剖析

addFirst()

addFirst(E e)的作用是?em>Deque的首端插入元素,也就是在head的前面插入元素,在空间够且下标没有界的情况下Q只需要将elements[--head] = e卛_?/p>

ArrayDeque_addFirst.png

实际需要考虑Q?.I间是否够用Q以?.下标是否界的问题。上图中Q如?code>head?code>0之后接着调用addFirst()Q虽然空余空间还够用Q但head?code>-1Q下标越界了。下列代码很好的解决了这两个问题?br />

//addFirst(E e)
public void addFirst(E e) {
    if (e == null)//不允许放入null
        throw new NullPointerException();
    elements[head = (head - 1) & (elements.length - 1)] = e;//2.下标是否界
    if (head == tail)//1.I间是否够用
        doubleCapacity();//扩容
}
上述代码我们看到Q?/span>I间问题是在插入之后解决?/strong>Q因?/span>tailL指向下一个可插入的空位,也就意味着elements数组臛_有一个空位,所以插入元素的时候不用考虑I间问题?/span>

下标界的处理解册v来非常简单,head = (head - 1) & (elements.length - 1)可以了Q?strong>q段代码相当于取余,同时解决?code>head值的情况。因?code>elements.length必需?code>2的指数倍,elements - 1是二进制低位全1Q跟head - 1怸之后pvC取模的作用,如果head - 1敎ͼ其实只可能是-1Q,则相当于对其取相对于elements.length的补码?/p>

下面再说说扩容函?code>doubleCapacity()Q其逻辑是申请一个更大的数组Q原数组的两倍)Q然后将原数l复制过厅R过E如下图所C:

ArrayDeque_doubleCapacity.png

图中我们看到Q复制分两次q行Q第一ơ复?code>head双的元素,W二ơ复?code>head左边的元素?br />

//doubleCapacity()
private void doubleCapacity() {
    assert head == tail;
    int p = head;
    int n = elements.length;
    int r = n - p; // head双元素的个?/span>
    int newCapacity = n << 1;//原空间的2?/span>
    if (newCapacity < 0)
        throw new IllegalStateException("Sorry, deque too big");
    Object[] a = new Object[newCapacity];
    System.arraycopy(elements, p, a, 0, r);//复制叛_部分Q对应上图中l色部分
    System.arraycopy(elements, 0, a, r, p);//复制左半部分Q对应上图中灰色部分
    elements = (E[])a;
    head = 0;
    tail = n;
}


addLast()

addLast(E e)的作用是?em>Deque的尾端插入元素,也就是在tail的位|插入元素,׃tailL指向下一个可以插入的IZQ因此只需?code>elements[tail] = e;卛_。插入完成后再检查空_如果I间已经用光Q则调用doubleCapacity()q行扩容?/p>

ArrayDeque_addLast.png

public void addLast(E e) {
    if (e == null)//不允许放入null
        throw new NullPointerException();
    elements[tail] = e;//赋?/span>
    if ( (tail = (tail + 1) & (elements.length - 1)) == head)//下标界处理
        doubleCapacity();//扩容
}
下标界处理方式addFirt()中已l讲q,不再赘述?/span>

pollFirst()

pollFirst()的作用是删除q返?em>Deque首端元素Q也xhead位置处的元素。如果容器不I,只需要直接返?code>elements[head]卛_Q当然还需要处理下标的问题。由?code>ArrayDeque中不允许攑օnullQ当elements[head] == nullӞ意味着容器为空?br />

public E pollFirst() {
    E result = elements[head];
    if (result == null)//null值意味着deque为空
        return null;
    elements[h] = null;//let GC work
    head = (head + 1) & (elements.length - 1);//下标界处理
    return result;
}

pollLast()

pollLast()的作用是删除q返?em>Deque元素Q也xtail位置前面的那个元素?br />

public E pollLast() {
    int t = (tail - 1) & (elements.length - 1);//tail的上一个位|是最后一个元?/span>
    E result = elements[t];
    if (result == null)//null值意味着deque为空
        return null;
    elements[t] = null;//let GC work
    tail = t;
    return result;
}

peekFirst()

peekFirst()的作用是q回但不删除Deque首端元素Q也xhead位置处的元素Q直接返?code>elements[head]卛_?br />

public E peekFirst() {
    return elements[head]; // elements[head] is null if deque empty
}

peekLast()

peekLast()的作用是q回但不删除Deque元素Q也xtail位置前面的那个元素?br />

public E peekLast() {
    return elements[(tail - 1) & (elements.length - 1)];
}


CarpenterLee 2016-05-07 18:30 发表评论
]]>
Java LinkedList源码剖析http://www.tkk7.com/CarpenterLee/archive/2016/05/04/430333.htmlCarpenterLeeCarpenterLeeWed, 04 May 2016 00:35:00 GMThttp://www.tkk7.com/CarpenterLee/archive/2016/05/04/430333.htmlhttp://www.tkk7.com/CarpenterLee/comments/430333.htmlhttp://www.tkk7.com/CarpenterLee/archive/2016/05/04/430333.html#Feedback3http://www.tkk7.com/CarpenterLee/comments/commentRss/430333.htmlhttp://www.tkk7.com/CarpenterLee/services/trackbacks/430333.htmlLinkedList

本文github地址

M介绍

LinkedList同时实现?em>List接口?em>Deque接口Q也是说它既可以看作一个顺序容器,又可以看作一个队列(QueueQ,同时又可以看作一个栈Q?em>StackQ。这L来,LinkedList直就是个全能冠军。当你需要用栈或者队列时Q可以考虑使用LinkedListQ一斚w是因为Java官方已经声明不徏议?em>Stackc,更遗憄是,Java里根本没有一个叫?em>Queue的类Q它是个接口名字Q。关于栈或队列,现在的首选是ArrayDequeQ它有着?em>LinkedListQ当作栈或队列用时Q有着更好的性能?/p>

LinkedList_base

LinkedList底层通过双向链表实现Q本节将着重讲解插入和删除元素时双向链表的l护q程Q也x之间解跟List接口相关的函敎ͼ而将Queue?em>Stack以及Deque相关的知识放在下一节讲。双向链表的每个节点用内部类Node表示?em>LinkedList通过first?code>last引用分别指向链表的第一个和最后一个元素。注意这里没有所谓的哑元Q当链表为空的时?code>first?code>last都指?code>null?br />

//Node内部c?/span>
private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

LinkedList的实现方式决定了所有跟下标相关的操作都是线性时_而在首段或者末ֈ除元素只需要常数时间。ؓq求效率LinkedList没有实现同步QsynchronizedQ,如果需要多个线Eƈ发访问,可以先采?code>Collections.synchronizedList()Ҏ对其q行包装?/p>

Ҏ剖析

add()

add()Ҏ有两个版本,一个是add(E e)Q该Ҏ?em>LinkedList的末插入元素,因ؓ?code>last指向链表末尾Q在末尾插入元素的花Ҏ常数旉。只需要简单修改几个相兛_用即可;另一个是add(int index, E element)Q该Ҏ是在指定下表处插入元素,需要先通过U性查找找到具体位|,然后修改相关引用完成插入操作?/p>

LinkedList_add

l合上图Q可以看?code>add(E e)的逻辑非常单?br />

//add(E e)
public boolean add(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;//原来链表为空Q这是插入的W一个元?/span>
    else
        l.next = newNode;
    size++;
    return true;
}


add(int index, E element)
的逻辑E显复杂Q可以分成两部,1.先根据index扑ֈ要插入的位置Q?.修改引用Q完成插入操作?br />

//add(int index, E element)
public void add(int index, E element) {
    checkPositionIndex(index);//index >= 0 && index <= size;
    if (index == size)//插入位置是末,包括列表为空的情?/span>
        add(element);
    else{
        Node<E> succ = node(index);//1.先根据index扑ֈ要插入的位置
        
//2.修改引用Q完成插入操作?/span>
        final Node<E> pred = succ.prev;
        final Node<E> newNode = new Node<>(pred, e, succ);
        succ.prev = newNode;
        if (pred == null)//插入位置?
            first = newNode;
        else
            pred.next = newNode;
        size++;
    }
}

上面代码中的node(int index)函数有一点小的trickQ因为链表双向的Q可以从开始往后找Q也可以从结־前找Q具体朝那个方向扑֏决于条gindex < (size >> 1)Q也xindex是靠q前端还是后端?/p>

remove()

remove()Ҏ也有两个版本Q一个是删除跟指定元素相{的W一个元?code>remove(Object o)Q另一个是删除指定下标处的元素remove(int index)?/p>

LinkedList_remove

两个删除操作都要1.先找到要删除元素的引用,2.修改相关引用Q完成删除操作。在L被删元素引用的时?code>remove(Object o)调用的是元素?code>equalsҎQ?code>remove(int index)使用的是下标计数Q两U方式都是线性时间复杂度。在步骤2中,两个revome()Ҏ都是通过unlink(Node<E> x)Ҏ完成的。这里需要考虑删除元素是第一个或者最后一个时的边界情c?br />

//unlink(Node<E> x)Q删除一个Node
E unlink(Node<E> x) {
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;
    if (prev == null) {//删除的是W一个元?/span>
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }
    if (next == null) {//删除的是最后一个元?/span>
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }
    x.item = null;//let GC work
    size--;
    return element;
}

get()

get(int index)得到指定下标处元素的引用Q通过调用上文中提到的node(int index)Ҏ实现?br />

public E get(int index) {
    checkElementIndex(index);//index >= 0 && index < size;
    return node(index).item;
}

set()

set(int index, E element)Ҏ指定下标处的元素修Ҏ指定|也是先通过node(int index)扑ֈ对应下表元素的引用,然后修改Node?code>item的倹{?br />

public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;//替换新?/span>
    return oldVal;
}


CarpenterLee 2016-05-04 08:35 发表评论
]]>
վ֩ģ壺 Ʒһ| йһػƵƬ| ޾Ʒר2| ޷츾| պŷһ| Ůڵվ | av벻þ| Ļվ| 99re߾ƷƵ| ˾ҹƷƵ߹ۿ | þñѵӰˬˬˬ| hƵ߹ۿ| һaɫƬþٸһHƬѷ| ÿĵӰվһ| þþþùɫAVѿͼƬ| ðѾƷƵ| ɫ¸avվ| þþþþav| ҹƵվ| 69paoǿѸ| ŷɫƷƵ| ޹| ԭƷav| ˳ɵӰվƷ| ƵС˵| 99߹ۿƷ99| fc2ѹƵ18| ˳վѲ| ޹˾žۺ| ޳av| ҹAVëƬþ| ĻƵѹۿ| **һëƬ| Ѹ| һëƬaѲɫӰ | þþþAVվ| ޹һ߹ۿ | þþþþþѿ| 97Ƶѹۿ2| ѹۿ| ˳ɵӰ߹ۿ|