﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>语源科技BlogJava-未来不遥远</title><link>http://www.blogjava.net/ArcticOcean/</link><description>Just do it！</description><language>zh-cn</language><lastBuildDate>Sun, 12 Apr 2026 11:27:50 GMT</lastBuildDate><pubDate>Sun, 12 Apr 2026 11:27:50 GMT</pubDate><ttl>60</ttl><item><title>杂想--关于职业迷茫</title><link>http://www.blogjava.net/ArcticOcean/archive/2010/09/01/330469.html</link><dc:creator>Cool Jazz</dc:creator><author>Cool Jazz</author><pubDate>Tue, 31 Aug 2010 16:19:00 GMT</pubDate><guid>http://www.blogjava.net/ArcticOcean/archive/2010/09/01/330469.html</guid><wfw:comment>http://www.blogjava.net/ArcticOcean/comments/330469.html</wfw:comment><comments>http://www.blogjava.net/ArcticOcean/archive/2010/09/01/330469.html#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://www.blogjava.net/ArcticOcean/comments/commentRss/330469.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ArcticOcean/services/trackbacks/330469.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 就像每个技术人员一样，我也遇到了职业迷茫期。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在一个半大不小的公司做研发，学历比本科高些，技术自认为可以（也许有人不这么认为吧）。时间过了一年。在一个开发兼除了销售什么都干的名字换了又换的部门。这就是大体情况。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 公司还可以，算得上不错。我认为公司的文化，人员结构，还不错。我也比较认同。只是我所在的部门，由于偏重于项目，和销售，现场支持接触的 比较多。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 部门的情况大体是这样，研发、测试、支持和运维全部都有，部门分工不是很明确。近一年换了两次名称，负责我们的一把手换了（原因我猜可能是，但不做妄论，故此不谈）。我的老大是个大牛，java、VB、VC、net、ajax等。什么都会，数据库可能差点，但通常用的不在话下。只是老大在管理上既无建树。虽然如此，他也是我们部门的中轴，没有他，估计部门都运行不起来。在最近一次部门变动后，正在转管理中。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 老大人蛮好，没有一点架子。和他一起交流没有一点压力。我很喜欢这个。作为技术人员，我没有过人的口才。这也是我认为，没有得到老大的上司（以前的，做销售的）重视或者&#8220;赏识&#8221;的原因吧。没有敢和领导做很多交流。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在大的公司政策下，部门随着变动，一个比我后来的小子，靠着他四处乱谈的口才，坐上了&#8220;研发经理&#8221;的位子。当时我也有机会，只是我在为政策房手续，紧接着又为结婚忙，几乎无暇估计这点升值机会。再加上老大上司&#8220;看人听说&#8221;的看人本领，就和老大一起定了他做。不过说实话，他很能&#8220;说&#8221;，只是很多话，你不仔细回想的话，很可能就认为是真的。经常一说一变。刚来时很我搞的很好，工作中也是能忽悠就忽悠过去。有时也见他加班。尤其是在要升职的那段时间。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 做上研发经理后，也是按着原来的的无管理路子，对现场的问题能拖久拖。现在就着上级给部门培训梳理的过程，对我仗势压人。哎，谁让我比他先来，比他。。。不说他了。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 相比他而言，我需要学习的地方，一是口才，当然不能学他不诚实的&#8220;忽悠&#8221;，要善于和人沟通交流；诚信是一个人立人的根本。人可以诚实，但一定要灵活，否则就是传说中的&#8220;傻&#8221;；二是善于抓机会。和领导搞关系。搞关系这点我不在行，更别提拍马屁了。说实话，谁也能做，只是在我的价值观，和性格上说，我不愿意去那样做。前朝的太监，当今的宰相。也是现在常有的现象。只是我不愿意去做。还是学和别人沟通交流的，在善意的基础上。俗话说嘛,害人之心不可有，防人之心不可无。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实，我也想过离职。但对于这样一个公司，有点舍不得。还由于，我老婆还没有毕业，没有正式获得劳动合同。我不能在她还没有稳定之时，就开始离职另选它路。还有我来这个公司，也是由于我的某些贵人，我不想背负他们。在她稳定之后，我在考虑这个吧。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 面对迷茫时，这样去解决：&nbsp;职业迷茫--&gt;分析原因--&gt;以史为鉴--&gt;求解--&gt;明确方向--&gt;行动<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 编程不是最终目的，语言不是万能。在目前的中国，人不能只靠代码吃饭。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 人生也许就是这样，总在一个又一个的烦恼和偶尔的快乐中度过。在此过程中，或许你会发现，哦，长大了。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 笔于&nbsp;九月一日晨零时十八分
<img src ="http://www.blogjava.net/ArcticOcean/aggbug/330469.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ArcticOcean/" target="_blank">Cool Jazz</a> 2010-09-01 00:19 <a href="http://www.blogjava.net/ArcticOcean/archive/2010/09/01/330469.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>HashMap原理、源码、实践</title><link>http://www.blogjava.net/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.blogjava.net/ArcticOcean/archive/2010/08/09/328316.html</guid><wfw:comment>http://www.blogjava.net/ArcticOcean/comments/328316.html</wfw:comment><comments>http://www.blogjava.net/ArcticOcean/archive/2010/08/09/328316.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ArcticOcean/comments/commentRss/328316.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ArcticOcean/services/trackbacks/328316.html</trackback:ping><description><![CDATA[HashMap是一种十分常用的数据结构，作为一个应用开发人员，对其原理、实现的加深理解有助于更高效地进行数据存取。本文所用的jdk版本为1.5。 <br />
<br />
<span style="font-size: large"><strong>使用HashMap</strong></span> <br />
<br />
《Effective JAVA》中认为，99%的情况下，当你覆盖了equals方法后，请务必覆盖hashCode方法。默认情况下，这两者会采用Object的&#8220;原生&#8221;实现方式，即： <br />
<br />
<div class="dp-highlighter">
<div class="tools">Java代码：<a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#"></a></div>
<ol class="dp-j">
    <li><span><span class="keyword">protected</span><span>&nbsp;</span><span class="keyword">native</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;hashCode(); &nbsp;&nbsp;</span></span>
    <li><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">boolean</span><span>&nbsp;equals(Object&nbsp;obj)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;(</span><span class="keyword">this</span><span>&nbsp;==&nbsp;obj); &nbsp;&nbsp;</span></span>
    <li><span>}&nbsp;&nbsp;</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方法的定义用到了native关键字，表示它是由C或C++采用较为底层的方式来实现的，你可以认为它返回了该对象的内存地址；而缺省equals则认为，只有当两者引用同一个对象时，才认为它们是相等的。如果你只是覆盖了equals()而没有重新定义hashCode()，在读取HashMap的时候，除非你使用一个与你保存时引用完全相同的对象作为key值，否则你将得不到该key所对应的值。 <br />
<br />
另一方面，你应该尽量避免使用&#8220;可变&#8221;的类作为HashMap的键。如果你将一个对象作为键值并保存在HashMap中，之后又改变了其状态，那么HashMap就会产生混乱，你所保存的值可能丢失（尽管遍历集合可能可以找到）。可参考<em><a href="http://www.ibm.com/developerworks/cn/java/j-jtp02183/" 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实际上是一个数组和链表的结合体，利用数组来模拟一个个桶（类似于Bucket Sort）以快速存取不同hashCode的key，对于相同hashCode的不同key，再调用其equals方法从List中提取出和key所相对应的value。 <br />
<br />
JAVA中hashMap的初始化主要是为initialCapacity和loadFactor这两个属性赋值。前者表示hashMap中用来区分不同hash值的key空间长度，后者是指定了当hashMap中的元素超过多少的时候，开始自动扩容，。默认情况下initialCapacity为16，loadFactor为0.75，它表示一开始hashMap可以存放16个不同的hashCode，当填充到第12个的时候，hashMap会自动将其key空间的长度扩容到32，以此类推；这点可以从源码中看出来： <br />
<br />
<div class="dp-highlighter">
<div class="bar">Java代码：</div>
<ol class="dp-j">
    <li><span><span class="keyword">void</span><span>&nbsp;addEntry(</span><span class="keyword">int</span><span>&nbsp;hash,&nbsp;K&nbsp;key,&nbsp;V&nbsp;value,&nbsp;</span><span class="keyword">int</span><span>&nbsp;bucketIndex)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;Entry&lt;K,V&gt;&nbsp;e&nbsp;=&nbsp;table[bucketIndex]; &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;table[bucketIndex]&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;Entry&lt;K,V&gt;(hash,&nbsp;key,&nbsp;value,&nbsp;e); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(size++&nbsp;&gt;=&nbsp;threshold) &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;resize(</span><span class="number">2</span><span>&nbsp;*&nbsp;table.length); &nbsp;&nbsp;</span></span>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">void addEntry(int hash, K key, V value, int bucketIndex) {
Entry&lt;K,V&gt; e = table[bucketIndex];
table[bucketIndex] = new Entry&lt;K,V&gt;(hash, key, value, e);
if (size++ &gt;= threshold)
resize(2 * table.length);
}
</pre>
<br />
<br />
而每当hashMap扩容后，内部的每个元素存放的位置都会发生变化（因为元素的最终位置是其hashCode对key空间长度取模而得），因此resize方法中又会调用transfer函数，用来重新分配内部的元素；这个过程成为rehash，是十分消耗性能的，因此在可预知元素的个数的情况下，一般应该避免使用缺省的initialCapacity，而是通过构造函数为其指定一个值。例如我们可能会想要将数据库查询所得1000条记录以某个特定字段（比如ID）为key缓存在hashMap中，为了提高效率、避免rehash，可以直接指定initialCapacity为2048。 <br />
<br />
另一个值得注意的地方是，hashMap其key空间的长度一定为2的N次方，这一点可以从一下源码中看出来： <br />
<div class="dp-highlighter">
<div class="tools">Java代码：</div>
<ol class="dp-j">
    <li><span><span class="keyword">int</span><span>&nbsp;capacity&nbsp;=&nbsp;</span><span class="number">1</span><span>; &nbsp;&nbsp;</span></span>
    <li><span class="keyword">while</span><span>&nbsp;(capacity&nbsp;&lt;&nbsp;initialCapacity)&nbsp; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;capacity&nbsp;&lt;&lt;=&nbsp;</span><span class="number">1</span><span>;&nbsp;&nbsp;</span></span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">int capacity = 1;
while (capacity &lt; initialCapacity)
capacity &lt;&lt;= 1;
</pre>
<br />
<br />
即使我们在构造函数中指定的initialCapacity不是2的平方数，capacity还是会被赋值为2的N次方。 <br />
<br />
为什么Sun Microsystem的工程师要将hashMap key空间的长度设为2的N次方呢？这里参考R.W.Floyed给出的衡量散列思想的三个标准： <br />
<br />
<ul><br />
    一个好的hash算法的计算应该是非常快的 <br />
    一个好的hash算法应该是冲突极小化 <br />
    如果存在冲突,应该是冲突均匀化 <br />
</ul>
<br />
<br />
为了将各元素的hashCode保存至长度为Length的key数组中，一般采用取模的方式，即index = hashCode % Length。不可避免的，存在多个不同对象的hashCode被安排在同一位置，这就是我们平时所谓的&#8220;冲突&#8221;。如果仅仅是考虑元素均匀化与冲突极小化，似乎应该将Length取为素数（尽管没有明显的理论来支持这一点，但数学家们通过大量的实践得出结论，对素数取模的产生结果的无关性要大于其它数字）。为此，Craig Larman and Rhett Guthrie《Java Performence》中对此也大加抨击。为了弄清楚这个问题，Bruce Eckel（Thinking in JAVA的作者）专程采访了java.util.hashMap的作者Joshua Bloch，并将他采用这种设计的原因放到了网上（<a href="http://grunt1223.javaeye.com/blog/<em>http://www.roseindia.net/javatutorials/javahashmap.shtml</em>" target=_blank><em>http://www.roseindia.net/javatutorials/javahashmap.shtml</em></a>） 。 <br />
<br />
上述设计的原因在于，取模运算在包括JAVA在内的大多数语言中的效率都十分低下，而当除数为2的N次方时，取模运算将退化为最简单的位运算，其效率明显提升（按照Bruce Eckel给出的数据，大约可以提升5～8倍） 。看看JDK中是如何实现的： <br />
<br />
<div class="dp-highlighter">
<div class="tools">Java代码：</div>
<ol class="dp-j">
    <li><span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;indexFor(</span><span class="keyword">int</span><span>&nbsp;h,&nbsp;</span><span class="keyword">int</span><span>&nbsp;length)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;h&nbsp;&amp;&nbsp;(length-</span><span class="number">1</span><span>); &nbsp;&nbsp;</span></span>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">static int indexFor(int h, int length) {
return h &amp; (length-1);
}
</pre>
<br />
<br />
当key空间长度为2的N次方时，计算hashCode为h的元素的索引可以用简单的与操作来代替笨拙的取模操作！假设某个对象的hashCode为35（二进制为100011），而hashMap采用默认的initialCapacity（16），那么indexFor计算所得结果将会是100011 &amp; 1111 = 11，即十进制的3，是不是恰好是35 Mod 16。 <br />
<br />
上面的方法有一个问题，就是它的计算结果仅有对象hashCode的低位决定，而高位被统统屏蔽了；以上面为例，19（10011）、35（100011）、67（1000011）等就具有相同的结果。针对这个问题， Joshua Bloch采用了&#8220;防御性编程&#8221;的解决方法，在使用各对象的hashCode之前对其进行二次Hash，参看JDK中的源码： <br />
<br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;hash(Object&nbsp;x)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;h&nbsp;=&nbsp;x.hashCode(); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h&nbsp;+=&nbsp;~(h&nbsp;&lt;&lt;&nbsp;</span><span class="number">9</span><span>); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h&nbsp;^=&nbsp;&nbsp;(h&nbsp;&gt;&gt;&gt;&nbsp;</span><span class="number">14</span><span>); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h&nbsp;+=&nbsp;&nbsp;(h&nbsp;&lt;&lt;&nbsp;</span><span class="number">4</span><span>); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;h&nbsp;^=&nbsp;&nbsp;(h&nbsp;&gt;&gt;&gt;&nbsp;</span><span class="number">10</span><span>); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;h; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">static int hash(Object x) {
int h = x.hashCode();
h += ~(h &lt;&lt; 9);
h ^=  (h &gt;&gt;&gt; 14);
h +=  (h &lt;&lt; 4);
h ^=  (h &gt;&gt;&gt; 10);
return h;
}
</pre>
<br />
<br />
采用这种旋转Hash函数的主要目的是让原有hashCode的高位信息也能被充分利用，且兼顾计算效率以及数据统计的特性，其具体的原理已超出了本文的领域。 <br />
<br />
加快Hash效率的另一个有效途径是编写良好的自定义对象的HashCode，String的实现采用了如下的计算方法： <br />
<br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span class="keyword">for</span><span>&nbsp;(</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;</span><span class="number">0</span><span>;&nbsp;i&nbsp;&lt;&nbsp;len;&nbsp;i++)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>h&nbsp;=&nbsp;</span><span class="number">31</span><span>*h&nbsp;+&nbsp;val[off++]; &nbsp;&nbsp;</span></span>
    <li><span>} &nbsp;&nbsp;</span>
    <li><span>hash&nbsp;=&nbsp;h;&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">for (int i = 0; i &lt; len; i++) {
h = 31*h + val[off++];
}
hash = h;
</pre>
<br />
<br />
这种方法HashCode的计算方法可能最早出现在Brian W. Kernighan和Dennis M. Ritchie的《The C Programming Language》中，被认为是性价比最高的算法（又被称为times33算法，因为C中乘数常量为33，JAVA中改为31），实际上，包括List在内的大多数的对象都是用这种方法计算Hash值。 <br />
<br />
另一种比较特殊的hash算法称为布隆过滤器，它以牺牲细微精度为代价，换来存储空间的大量节俭，常用于诸如判断用户名重复、是否在黑名单上等等，可以参考李开复的数学之美系列第13篇（<em><a href="http://googlechinablog.com/2006/08/blog-post.html" target="_blank">http://googlechinablog.com/2006/08/blog-post.html</a></em>） <br />
<br />
<span style="font-size: large"><strong>Fail-Fast机制</strong></span> <br />
<br />
众所周知，HashMap不是线程安全的集合类。但在某些容错能力较好的应用中，如果你不想仅仅因为1%的可能性而去承受hashTable的同步开销，则可以考虑利用一下HashMap的Fail-Fast机制，其具体实现如下： <br />
<br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span>Entry&lt;K,V&gt;&nbsp;nextEntry()&nbsp;{&nbsp; &nbsp;&nbsp;</span></span>
    <li><span class="keyword">if</span><span>&nbsp;(modCount&nbsp;!=&nbsp;expectedModCount) &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">throw</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;ConcurrentModificationException(); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&#8230;&#8230; &nbsp;&nbsp;</span>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">Entry&lt;K,V&gt; nextEntry() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
&#8230;&#8230;
}
</pre>
<br />
<br />
其中modCount为HashMap的一个实例变量，并且被声明为volatile，表示任何线程都可以看到该变量被其它线程修改的结果（根据JVM内存模型的优化，每一个线程都会存一份自己的工作内存，此工作内存的内容与本地内存并非时时刻刻都同步，因此可能会出现线程间的修改不可见的问题） 。使用Iterator开始迭代时，会将modCount的赋值给expectedModCount，在迭代过程中，通过每次比较两者是否相等来判断HashMap是否在内部或被其它线程修改。HashMap的大多数修改方法都会改变ModCount，参考下面的源码： <br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span class="keyword">public</span><span>&nbsp;V&nbsp;put(K&nbsp;key,&nbsp;V&nbsp;value)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;K&nbsp;k&nbsp;=&nbsp;maskNull(key); &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;hash&nbsp;=&nbsp;hash(k); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">int</span><span>&nbsp;i&nbsp;=&nbsp;indexFor(hash,&nbsp;table.length); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">for</span><span>&nbsp;(Entry&lt;K,V&gt;&nbsp;e&nbsp;=&nbsp;table[i];&nbsp;e&nbsp;!=&nbsp;</span><span class="keyword">null</span><span>;&nbsp;e&nbsp;=&nbsp;e.next)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(e.hash&nbsp;==&nbsp;hash&nbsp;&amp;&amp;&nbsp;eq(k,&nbsp;e.key))&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;V&nbsp;oldValue&nbsp;=&nbsp;e.value; &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.value&nbsp;=&nbsp;value; &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;e.recordAccess(</span><span class="keyword">this</span><span>); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;oldValue; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;modCount++; &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addEntry(hash,&nbsp;k,&nbsp;value,&nbsp;i); &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">null</span><span>; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</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&lt;K,V&gt; e = table[i]; e != null; e = e.next) {
if (e.hash == hash &amp;&amp; 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方法为例，每次往HashMap中添加元素都会导致modCount自增。其它诸如remove、clear方法也都包含类似的操作。 <br />
从上面可以看出，HashMap所采用的Fail-Fast机制本质上是一种乐观锁机制，通过检查状态——没有问题则忽略——有问题则抛出异常的方式，来避免线程同步的开销，下面给出一个在单线程环境下发生Fast-Fail的例子： <br />
<br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span class="keyword">class</span><span>&nbsp;Test&nbsp;{&nbsp;&nbsp; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;{&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.util.HashMap&lt;Object,String&gt;&nbsp;map=</span><span class="keyword">new</span><span>&nbsp;java.util.HashMap&lt;Object,String&gt;();&nbsp;&nbsp; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map.put(</span><span class="keyword">new</span><span>&nbsp;Object(),&nbsp;</span><span class="string">"a"</span><span>);&nbsp;&nbsp; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map.put(</span><span class="keyword">new</span><span>&nbsp;Object(),&nbsp;</span><span class="string">"b"</span><span>);&nbsp;&nbsp; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;java.util.Iterator&lt;Object&gt;&nbsp;it=map.keySet().iterator();&nbsp;&nbsp; &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">while</span><span>(it.hasNext()){&nbsp;&nbsp; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;it.next();&nbsp;&nbsp; &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map.put(</span><span class="string">""</span><span>,&nbsp;</span><span class="string">""</span><span>);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(map.size());&nbsp;&nbsp; &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp; &nbsp;&nbsp;</span>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">class Test {
public static void main(String[] args) {
java.util.HashMap&lt;Object,String&gt; map=new java.util.HashMap&lt;Object,String&gt;();
map.put(new Object(), "a");
map.put(new Object(), "b");
java.util.Iterator&lt;Object&gt; it=map.keySet().iterator();
while(it.hasNext()){
it.next();
map.put("", "");
System.out.println(map.size());
}
}</pre>
<br />
<br />
运行上面的代码会抛出java.util.ConcurrentModificationException，因为在迭代过程中修改了HashMap内部的元素导致modCount自增。若将上面代码中 map.put(new Object(), "b") 这句注释掉，程序会顺利通过，因为此时HashMap中只包含一个元素，经过一次迭代后已到了尾部，所以不会出现问题，也就没有抛出异常的必要了。 <br />
在通常并发环境下，还是建议采用同步机制。这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象，则应该使用 Collections.synchronizedMap 方法来&#8220;包装&#8221;该映射。最好在创建时完成这一操作，以防止意外的非同步访问。 <br />
<br />
<span style="font-size: large"><strong>LinkedHashMap</strong></span> <br />
<br />
遍历HashMap所得到的数据是杂乱无章的，这在某些情况下客户需要特定遍历顺序时是十分有用的。比如，这种数据结构很适合构建 LRU 缓存。调用 put 或 get 方法将会访问相应的条目（假定调用完成后它还存在）。putAll 方法以指定映射的条目集合迭代器提供的键-值映射关系的顺序，为指定映射的每个映射关系生成一个条目访问。Sun提供的J2SE说明文档特别规定任何其他方法均不生成条目访问，尤其，collection 集合类的操作不会影响底层映射的迭代顺序。 <br />
<br />
LinkedHashMap的实现与 HashMap 的不同之处在于，前者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序，该迭代顺序通常就是集合中元素的插入顺序。该类定义了header、before与after三个属性来表示该集合类的头与前后&#8220;指针&#8221;，其具体用法类似于数据结构中的双链表，以删除某个元素为例： <br />
<br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span class="keyword">private</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;remove()&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;before.after&nbsp;=&nbsp;after; &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;after.before&nbsp;=&nbsp;before; &nbsp;&nbsp;</span>
    <li><span>}&nbsp;&nbsp;</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 />
显然，由于增加了维护链接列表的开支，其性能要比 HashMap 稍逊一筹，不过有一点例外：LinkedHashMap的迭代所需时间与其的所包含的元素成比例；而HashMap 迭代时间很可能开支较大，因为它所需要的时间与其容量（分配给Key空间的长度）成比例。一言以蔽之，随机存取用HashMap，顺序存取或是遍历用LinkedHashMap。 <br />
<br />
LinkedHashMap还重写了removeEldestEntry方法以实现自动清除过期数据的功能，这在HashMap中是无法实现的，因为后者其内部的元素是无序的。默认情况下，LinkedHashMap中的removeEldestEntry的作用被关闭，其具体实现如下： <br />
<br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span class="keyword">protected</span><span>&nbsp;</span><span class="keyword">boolean</span><span>&nbsp;removeEldestEntry(Map.Entry&lt;K,V&gt;&nbsp;eldest)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">false</span><span>; &nbsp;&nbsp;</span></span>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">protected boolean removeEldestEntry(Map.Entry&lt;K,V&gt; eldest) {
return false;
}</pre>
<br />
<br />
可以使用如下的代码覆盖removeEldestEntry： <br />
<br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span class="keyword">private</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">int</span><span>&nbsp;MAX_ENTRIES&nbsp;=&nbsp;</span><span class="number">100</span><span>; &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;</span>
    <li><span class="keyword">protected</span><span>&nbsp;</span><span class="keyword">boolean</span><span>&nbsp;removeEldestEntry(Map.Entry&nbsp;eldest)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">return</span><span>&nbsp;size()&nbsp;&gt;&nbsp;MAX_ENTRIES; &nbsp;&nbsp;</span></span>
    <li><span>}&nbsp;&nbsp;</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() &gt; MAX_ENTRIES;
}
</pre>
<br />
<br />
它表示，刚开始，LinkedHashMap中的元素不断增长；当它内部的元素超过MAX_ENTRIES（100）后，每当有新的元素被插入时，都会自动删除双链表中最前端（最旧）的元素，从而保持LinkedHashMap的长度稳定。 <br />
<br />
缺省情况下，LinkedHashMap采取的更新策略是类似于队列的FIFO，如果你想实现更复杂的更新逻辑比如LRU（最近最少使用） 等，可以在构造函数中指定其accessOrder为true，因为的访问元素的方法（get）内部会调用一个&#8220;钩子&#8221;，即recordAccess，其具体实现如下： <br />
<br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span class="keyword">void</span><span>&nbsp;recordAccess(HashMap&lt;K,V&gt;&nbsp;m)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;LinkedHashMap&lt;K,V&gt;&nbsp;lm&nbsp;=&nbsp;(LinkedHashMap&lt;K,V&gt;)m; &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">if</span><span>&nbsp;(lm.accessOrder)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;lm.modCount++; &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;remove(); &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;addBefore(lm.header); &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span>
    <li><span>}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">void recordAccess(HashMap&lt;K,V&gt; m) {
LinkedHashMap&lt;K,V&gt; lm = (LinkedHashMap&lt;K,V&gt;)m;
if (lm.accessOrder) {
lm.modCount++;
remove();
addBefore(lm.header);
}
}</pre>
<br />
<br />
上述代码主要实现了这样的功能：如果accessOrder被设置为true，则每次访问元素时，都将该元素移至headr的前面，即链表的尾部。将removeEldestEntry与accessOrder一起使用，就可以实现最基本的内存缓存，具体代码可参考<a href="http://grunt1223.javaeye.com/blog/<em>http://bluepopopo.javaeye.com/blog/180236</em>" 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教材教导我们不要去干预JVM的垃圾回收机制，但JAVA中确实存在着与其密切相关的四种引用：强引用、软引用、弱引用以及幻象引用。 <br />
<br />
JAVA中默认的HashMap采用的是采用类似于强引用的强键来管理的，这意味着即使作为key的对象已经不存在了（指没有任何一个引用指向它），也仍然会保留在HashMap中，在某些情况下（例如内存缓存）中，这些过期的条目可能会造成内存泄漏等问题。 <br />
<br />
WeakHashMap采用的策略是，只要作为key的对象已经不存在了（超出生命周期），就不会阻止垃圾收集器清空此条目，即使当前机器的内存并不紧张。不过，由于GC是一个优先级很低的线程，因此不一定会很快发现那些只具有弱引用的对象，除非你显示地调用它，可以参考下面的例子： <br />
<br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;Map&lt;String,&nbsp;String&gt;map&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;WeakHashMap&lt;String,&nbsp;String&gt;(); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;map.put(</span><span class="keyword">new</span><span>&nbsp;String(</span><span class="string">"Alibaba"</span><span>),&nbsp;</span><span class="string">"alibaba"</span><span>); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">while</span><span>&nbsp;(map.containsKey(</span><span class="string">"Alibaba"</span><span>))&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="keyword">try</span><span>&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Thread.sleep(</span><span class="number">500</span><span>); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="keyword">catch</span><span>&nbsp;(InterruptedException&nbsp;ignored)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(</span><span class="string">"Checking&nbsp;for&nbsp;empty"</span><span>); &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.gc(); &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;&nbsp;</span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">public static void main(String[] args) {
Map&lt;String, String&gt;map = new WeakHashMap&lt;String, String&gt;();
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就退出了主线程，意味着GC在最近的一次垃圾回收周期中清除了new String(&#8220;Alibaba&#8221;),同时WeakHashMap也做出了及时的反应，将该键对应的条目删除了。如果将map的类型改为HashMap的话，由于其内部采用的是强引用机制，因此即使GC被显示调用，map中的条目依然存在，程序会不断地打出Checking for empty字样。另外，在使用WeakHashMap的情况下，若是将 <br />
<br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span>map.put(</span><span class="keyword">new</span><span>&nbsp;String(</span><span class="string">"Alibaba"</span><span>),&nbsp;</span><span class="string">"alibaba"</span><span>);&nbsp;&nbsp;&nbsp;</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 class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span>map.put(</span><span class="string">"Alibaba"</span><span>,&nbsp;</span><span class="string">"alibaba"</span><span>);&nbsp;&nbsp;&nbsp;</span></span> </li>
</ol>
</div>
<pre class="java" style="display: none" name="code">map.put("Alibaba", "alibaba"); </pre>
<br />
<br />
程序还是会不断输出Checking for empty。这与前面我们分析的WeakHashMap的弱引用机制并不矛盾，因为JVM为了减小重复创建和维护多个相同String的开销，其内部采用了蝇量模式（《JAVA与模式》），此时的&#8220;Alibaba&#8221;是存放在常量池而非堆中的，因此即使没有对象指向&#8220;Alibaba&#8221;，它也不会被GC回收。弱引用特别适合以下对象：占用大量内存，但通过垃圾回收功能回收以后很容易重新创建。 <br />
<br />
介于HashMap和WeakHashMap之中的是SoftHashMap，它所采用的软引用的策略指的是，垃圾收集器并不像其收集弱可及的对象一样尽量地收集软可及的对象，相反，它只在真正 &#8220;需要&#8221; 内存时才收集软可及的对象。软引用对于垃圾收集器来说是一种&#8220;睁一只眼，闭一只眼&#8221;方式，即 &#8220;只要内存不太紧张，我就会保留该对象。但是如果内存变得真正紧张了，我就会去收集并处理这个对象。&#8221; 就这一点看，它其实要比WeakHashMap更适合于实现缓存机制。遗憾的是，JAVA中并没有实现相关的SoftHashMap类（Apache和Google提供了第三方的实现），但它却是提供了两个十分重要的类java.lang.ref.SoftReference以及ReferenceQueue，可以在对象应用状态发生改变是得到通知，可以参考com.alibaba.common.collection.SofthashMap中processQueue方法的实现: <br />
<br />
<div class="dp-highlighter">
<div class="tools"><a title="复制代码" onclick="dp.sh.Toolbar.CopyToClipboard(this);return false;" href="http://grunt1223.javaeye.com/blog/544497#">Java代码：</a></div>
<ol class="dp-j">
    <li><span><span class="keyword">private</span><span>&nbsp;ReferenceQueue&nbsp;queue&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;ReferenceQueue(); &nbsp;&nbsp;</span></span>
    <li><span>ValueCell&nbsp;vc; &nbsp;&nbsp;</span>
    <li><span>Map&nbsp;hash&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;HashMap(initialCapacity,&nbsp;loadFactor); &nbsp;&nbsp;</span></span>
    <li><span>&#8230;&#8230; &nbsp;&nbsp;</span>
    <li><span class="keyword">while</span><span>&nbsp;((vc&nbsp;=&nbsp;(ValueCell)&nbsp;queue.poll())&nbsp;!=&nbsp;</span><span class="keyword">null</span><span>)&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span class="keyword">if</span><span>&nbsp;(vc.isValid())&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;hash.remove(vc.key); &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}&nbsp;</span><span class="keyword">else</span><span>&nbsp;{ &nbsp;&nbsp;</span></span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;valueCell.dropped--; &nbsp;&nbsp;</span>
    <li><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;</span>
    <li><span>} &nbsp;&nbsp;</span>
    <li><span>}&nbsp;&nbsp;</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);
&#8230;&#8230;
while ((vc = (ValueCell) queue.poll()) != null) {
if (vc.isValid()) {
hash.remove(vc.key);
} else {
valueCell.dropped--;
}
}
}</pre>
<br />
<br />
processQueue方法会在几乎所有SoftHashMap的方法中被调用到，JVM会通过ReferenceQueue的poll方法通知该对象已经过期并且当前的内存现状需要将它释放，此时我们就可以将其从hashMap中剔除。事实上，默认情况下，Alibaba的MemoryCache所使用的就是SoftHashMap。 <br />
<br />
来源：<a href="http://grunt1223.javaeye.com/blog/544497">http://grunt1223.javaeye.com/blog/544497</a><br />
<img src ="http://www.blogjava.net/ArcticOcean/aggbug/328316.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ArcticOcean/" target="_blank">Cool Jazz</a> 2010-08-09 15:14 <a href="http://www.blogjava.net/ArcticOcean/archive/2010/08/09/328316.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>转载：软件开发的一些感想</title><link>http://www.blogjava.net/ArcticOcean/archive/2010/08/04/327923.html</link><dc:creator>Cool Jazz</dc:creator><author>Cool Jazz</author><pubDate>Wed, 04 Aug 2010 04:34:00 GMT</pubDate><guid>http://www.blogjava.net/ArcticOcean/archive/2010/08/04/327923.html</guid><wfw:comment>http://www.blogjava.net/ArcticOcean/comments/327923.html</wfw:comment><comments>http://www.blogjava.net/ArcticOcean/archive/2010/08/04/327923.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ArcticOcean/comments/commentRss/327923.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ArcticOcean/services/trackbacks/327923.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看了Eric77的软件开发的一些感想（五年工作经验总结），很有感触。这个世界真是好大，自己作为个体真是太渺小，还有太多的东西需要学习。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 把他文章的部分转接过来，顺便自己也备注下，对自己有个参考。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一、需求分析,设计,开发,测试和项目管理整个流程：<br />
<p>对任何系统来说: 无非是做好3件事: IPO. 也就是Input,Process,Output. （偏重于设计，类似于计算机本身，作为总结本身还是蛮清楚的）</p>
<p>对需求分析来说: 你最重要的是搞明白,用户的需求.也就是搞清用户的输入(Input)和输出(Output)是什么,它的要求达到的功能(Process)是什么. 明白以后,你就可以写一些用户需求说明书,描述用例,输入输出处理异常什么的,或者做一个简单的Demo系统,拿去给用户看,看看这个界面是不是用户喜欢的,这个流程是不是用户所需要的等等.（注：如果加入点管理，就丰满了。还有以后的需求变更，要知道需要可能是不断变化的）</p>
<p>然后就是系统分析: 首先,你需要对用户的需求分模块,每个模块的IPO是什么,他们应采用什么架构,需要和那些模块交互,互相之间的接口是怎样的,需要使用什么技术.每个模块的运行环境是什么样子的,对效率,安全或者容错等特性是否要着重考虑.（注：考虑的蛮多的，加个词：系统的可扩展性）</p>
<p>接着就是系统设计: 每个模块的数据库要怎么设计,所用技术和架构决定后,要确定不同层次有哪些接口,比如说表现层,业务逻辑层,数据库访问层,相互之间怎么调用,定好框架和开发模式和格式,剩下的就是编码了..（注：说的似乎少了点）</p>
<p>编码部分: 首先团队要有一个良好的编码规范.并且对每个人提交的代码要review,我发现程序编多了,自己就成了一台计算,看到程序就会知道会出现什么结果,根本都不用跑起来.（注：编码的时候，多考虑下：复用和模式）</p>
<p>测试: 单元测试是否编写测试用例? Junit是个好工具.每个人都要保证好自己的代码没有问题. 整体测试就要整个流程的跑一下,看看需求实现的对不对,还有没有什么其他纰漏.&nbsp; .（注：测试分好多种，单元，压力，持久性、黑白盒等，有专门的测试部分或组就不一样了）</p>
<p>风险管理: 这一点很重要,要时刻知道自己的项目有什么风险,无论是人员的,技术的,时间的,还是协调上面的.都要心里有数,按时按周向领导汇报。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp; 以下部分——</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 记得我去IBM面试的时候,问过,以后有几条职业发展的方向,回答有三条,一条是IT Specialist,一条是Project Manager,还有一条是IT Architect. 我还曾要求以后能不能向IT Architect发展,那人说,Architect要熟悉很多技术呢,我问了两遍,他这样回答了两遍.后来给我订了Advisory I/T Specialist. 我当时很纳闷,我搞Java这么久,J2ME,J2SE,J2EE全都搞过,而且对Gof的&lt;Design Pattern&gt;也悟了5年,了熟于心,各种各样的经典书籍也看了不少,怎么不能向Architect发展呢. 现在我加入了IBM,进入一个香港的项目,过段时间要去香港培训几星期,才明白了,原来一个大项目会涉及到各种各样的语言,技术,什么Cobal(音译),forturn(音译),java,vb,C#,soa xx总线模式,mq,大型机开发,等,很多我只在书上看到过的古老语言,还有一些听都没听过的语言,框架,模式,全都出现了.我当时这个汗哪,我才发现我真的很无知.知识就是一个气球,你知道的越多,接触的越多就会发现自己越无知.所以这也是我要今天赶紧把这个写出来的原因. 我怕以后又有了变故,还要沉淀,需要时间.</p>
<p>&nbsp;</p>
<p>本文部分来自CSDN博客：<a href="http://blog.csdn.net/Eric77/archive/2010/08/01/5781573.aspx">http://blog.csdn.net/Eric77/archive/2010/08/01/5781573.aspx</a></p>
<img src ="http://www.blogjava.net/ArcticOcean/aggbug/327923.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ArcticOcean/" target="_blank">Cool Jazz</a> 2010-08-04 12:34 <a href="http://www.blogjava.net/ArcticOcean/archive/2010/08/04/327923.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>分布式计算框架Hadoop</title><link>http://www.blogjava.net/ArcticOcean/archive/2010/08/03/327836.html</link><dc:creator>Cool Jazz</dc:creator><author>Cool Jazz</author><pubDate>Tue, 03 Aug 2010 09:03:00 GMT</pubDate><guid>http://www.blogjava.net/ArcticOcean/archive/2010/08/03/327836.html</guid><wfw:comment>http://www.blogjava.net/ArcticOcean/comments/327836.html</wfw:comment><comments>http://www.blogjava.net/ArcticOcean/archive/2010/08/03/327836.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ArcticOcean/comments/commentRss/327836.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ArcticOcean/services/trackbacks/327836.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 本文大部分内容都是从官网<a title="Hadoop" href="http://hadoop.apache.org/"><span style="font-size: 14pt">Hadoop</span></a><span style="font-size: 14pt">上转来的。其中有篇</span><a title="介绍HDFS的pdf文档" href="http://hadoop.apache.org/common/docs/current/hdfs_design.pdf"><span style="font-size: 14pt">介绍HDFS的pdf文档</span></a><span style="font-size: 14pt">，对Hadoop介绍的比较全面。<br />
</span><span style="font-size: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 先说一下Hadoop的来龙去脉。谈到Hadoop就不得不提到</span><a title="Lucene" href="http://lucene.apache.org/"><span style="font-size: 14pt">Lucene</span></a><span style="font-size: 14pt">和</span><a title="Nutch" href="http://lucene.apache.org/nutch/"><span style="font-size: 14pt">Nutch</span></a><span style="font-size: 14pt">。首先，<span style="color: rgb(0,0,255)">Lucene并不是一个应用程序，而是提供了一个纯Java的高性能全文索引引擎工具包</span>，它可以方便的嵌入到各种实际应用中实现全文搜索/索引功能。<span style="color: rgb(0,0,255)">Nutch是一个应用程序，是一个以Lucene为基础实现的搜索引擎应用</span>，Lucene 为Nutch提供了文本搜索和索引的API，Nutch不光有搜索的功能，还有数据抓取的功能。在nutch0.8.0版本之前，Hadoop还属于 Nutch的一部分，而从nutch0.8.0开始，将其中实现的NDFS和MapReduce剥离出来成立一个新的开源项目，这就是Hadoop，而 nutch0.8.0版本较之以前的Nutch在架构上有了根本性的变化，那就是完全构建在Hadoop的基础之上了。在Hadoop中实现了 Google的GFS和MapReduce算法，使Hadoop成为了一个分布式的计算平台。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Hadoop并不仅仅是一个用于存储的分布式文件系统，而是设计用来在由通用计算设备组成的大型集群上执行分布式应用的框架。<br />
<br />
&nbsp;&nbsp;&nbsp;<span style="color: rgb(0,0,255)">Hadoop包含两个部分：</span><br />
<br />
&nbsp;&nbsp;&nbsp;<strong>1、HDFS<br />
</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;即Hadoop Distributed File System (Hadoop分布式文件系统)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HDFS 具有高容错性，并且可以被部署在低价的硬件设备之上。HDFS很适合那些有大数据集的应用，并且提供了对数据读写的高吞吐率。HDFS是一个 master/slave的结构，就通常的部署来说，在master上只运行一个Namenode，而在每一个slave上运行一个Datanode。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HDFS 支持传统的层次文件组织结构，同现有的一些文件系统在操作上很类似，比如你可以创建和删除一个文件，把一个文件从一个目录移到另一个目录，重命名等等操 作。Namenode管理着整个分布式文件系统，对文件系统的操作（如建立、删除文件和文件夹）都是通过Namenode来控制。&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面是HDFS的结构：</span><br />
<div align="center">
<div align="center"><img style="width: 748px; height: 561px" height="561" alt="" src="http://www.cnblogs.com/images/cnblogs_com/wayne1017/HDFSArch.JPG" width="748" border="0" /></div>
</div>
<br />
<span style="font-size: 14pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从上面的图中可以看 出，Namenode，Datanode，Client之间的通信都是建立在TCP/IP的基础之上的。当Client要执行一个写入的操作的时候，命令 不是马上就发送到Namenode，Client首先在本机上临时文件夹中缓存这些数据，当临时文件夹中的数据块达到了设定的Block的值（默认是 64M）时，Client便会通知Namenode，Namenode便响应Client的RPC请求，将文件名插入文件系统层次中并且在 Datanode中找到一块存放该数据的block，同时将该Datanode及对应的数据块信息告诉Client，Client便这些本地临时文件夹中 的数据块写入指定的数据节点。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;HDFS采取了副本策略，其目的是为了提高系统的可靠性，可用性。HDFS的副本放置策略是三个副本， 一个放在本节点上，一个放在同一机架中的另一个节点上，还有一个副本放在另一个不同的机架中的一个节点上。当前版本的hadoop0.12.0中还没有实 现，但是正在进行中，相信不久就可以出来了。<br />
<br />
&nbsp;&nbsp;&nbsp;<strong>2、MapReduce的实现<br />
</strong><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><a title="MapReduce" href="http://labs.google.com/papers/mapreduce.html"><span style="font-size: 14pt">MapReduce</span></a><span style="font-size: 14pt">是Google 的一项重要技术，它是一个编程模型，用以进行大数据量的计算。对于大数据量的计算，通常采用的处理手法就是并行计算。至少现阶段而言，对许多开发人员来 说，并行计算还是一个比较遥远的东西。MapReduce就是一种简化并行计算的编程模型，它让那些没有多少并行计算经验的开发人员也可以开发并行应用。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MapReduce的名字源于这个模型中的两项核心操作：Map和 Reduce。也许熟悉Functional Programming（</span><a title="函数式编程" href="http://chn.blogbeta.com/232.html"><span style="font-size: 14pt">函数式编程</span></a><span style="font-size: 14pt">） 的人见到这两个词会倍感亲切。简单的说来，Map是把一组数据一对一的映射为另外的一组数据，其映射的规则由一个函数来指定，比如对[1, 2, 3, 4]进行乘2的映射就变成了[2, 4, 6, 8]。Reduce是对一组数据进行归约，这个归约的规则由一个函数指定，比如对[1, 2, 3, 4]进行求和的归约得到结果是10，而对它进行求积的归约结果是24。<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;关于MapReduce的内容，建议看看孟岩的这篇</span><a title="MapReduce:The free lunch is not over!" href="http://www.mengyan.org/blog/archives/2006/11/15/138.html"><span style="font-size: 14pt">MapReduce:The Free Lunch Is Not Over!</span></a>&nbsp;这篇算是介绍的比较详细的。MapReduce的算法内容见Google文档：<a title="MapReduce.pdf" href="http://labs.google.com/papers/mapreduce-osdi04.pdf">MapReduce.pdf</a><br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 有关其它介绍Hadoop的文章建议看下：<a href="http://www.infoq.com/cn/articles/hadoop-intro" rel="permalink">分布式计算开源框架Hadoop介绍</a>&nbsp;。（what，why，how提的不错）<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 安装配置可以看：1、<a href="http://www.infoq.com/cn/articles/hadoop-config-tip" rel="permalink">Hadoop中的集群配置和使用技巧</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2、<a href="http://developer.51cto.com/art/201006/203882.htm">Hadoop应用之Hadoop安装篇</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 3、<a title="Hadoop安装部署指南" href="http://blog.csdn.net/Eric77/archive/2008/12/16/3530927.aspx">Hadoop安装部署指南</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 如果要开发的话，初步参考：<a href="http://www.infoq.com/cn/articles/hadoop-process-develop" rel="permalink">Hadoop基本流程与应用开发</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其中用到数据库的部分，在Hadoop的0.19.0开始支撑数据库访问，主要采用DBInputFormat来访问数据库。文章可见：<a href="http://jaguar13.javaeye.com/blog/683392">Hadoop中的数据库访问</a><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
 <img src ="http://www.blogjava.net/ArcticOcean/aggbug/327836.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ArcticOcean/" target="_blank">Cool Jazz</a> 2010-08-03 17:03 <a href="http://www.blogjava.net/ArcticOcean/archive/2010/08/03/327836.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>字符集编码研究</title><link>http://www.blogjava.net/ArcticOcean/articles/324841.html</link><dc:creator>Cool Jazz</dc:creator><author>Cool Jazz</author><pubDate>Wed, 30 Jun 2010 01:08:00 GMT</pubDate><guid>http://www.blogjava.net/ArcticOcean/articles/324841.html</guid><wfw:comment>http://www.blogjava.net/ArcticOcean/comments/324841.html</wfw:comment><comments>http://www.blogjava.net/ArcticOcean/articles/324841.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ArcticOcean/comments/commentRss/324841.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ArcticOcean/services/trackbacks/324841.html</trackback:ping><description><![CDATA[<h1 style="margin-bottom: 0pt; line-height: normal; text-align: center" align="center">--字符集编码</h1>
<p class="1">1. 概述</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">本文主要包括以下几个方面：编码基本知识，java，系统软件，url，工具软件等。</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">在下面的描述中，将以"中文"两个字为例，经查表可以知道其GB2312编码是"<u>d6d0 cec4</u>"，Unicode编码为"<u>4e2d 6587</u>"，UTF编码就是"<u>e4b8ad e69687</u>"。注意，这两个字没有iso8859-1编码，但可以用iso8859-1编码来"表示"。</p>
<p class="1">2. 编码基本知识</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">最早的编码是iso8859-1，和ascii编码相似。但为了方便表示各种各样的语言，逐渐出现了很多标准编码，重要的有如下几个。</p>
<p class="2">2.1. iso8859-1</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">属于单字节编码，最多能表示的字符范围是0-255，应用于英文系列。比如，字母'a'的编码为0x61=97。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">很明显，iso8859-1编码表示的字符范围很窄，无法表示中文字符。但是，由于是单字节编码，和计算机最基础的表示单位一致，所以很多时候，仍旧使用iso8859-1编码来表示。而且在很多协议上，默认使用该编码。比如，虽然"中文"两个字不存在iso8859-1编码，以gb2312编码为例，应该是"<u>d6d0 cec4</u>"两个字符，使用iso8859-1编码的时候则将它拆开为4个字节来表示："<u>d6 d0 ce c4</u>"（事实上，在进行存储的时候，也是以字节为单位处理的）。而如果是UTF编码，则是6个字节"<u>e4 b8 ad e6 96 87</u>"。很明显，这种表示方法还需要以另一种编码为基础。</p>
<p class="2">2.2. GB2312/GBK</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">这就是汉子的国标码，专门用来表示汉字，是双字节编码，而英文字母和iso8859-1一致（兼容iso8859-1编码）。其中gbk编码能够用来同时表示繁体字和简体字，而gb2312只能表示简体字，gbk是兼容gb2312编码的。</p>
<p class="2">2.3. unicode</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">这是最统一的编码，可以用来表示所有语言的字符，而且是定长双字节（也有四字节的）编码，包括英文字母在内。所以可以说它是不兼容iso8859-1编码的，也不兼容任何编码。不过，相对于iso8859-1编码来说，uniocode编码只是在前面增加了一个0字节，比如字母'a'为"<u>00 61</u>"。</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">需要说明的是，定长编码便于计算机处理（注意GB2312/GBK不是定长编码），而unicode又可以用来表示所有字符，所以在很多软件内部是使用unicode编码来处理的，比如java。</p>
<p class="2">2.4. UTF</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">考虑到unicode编码不兼容iso8859-1编码，而且容易占用更多的空间：因为对于英文字母，unicode也需要两个字节来表示。所以unicode不便于传输和存储。因此而产生了utf编码，utf编码兼容iso8859-1编码，同时也可以用来表示所有语言的字符，不过，utf编码是不定长编码，每一个字符的长度从1-6个字节不等。另外，utf编码自带简单的校验功能。一般来讲，英文字母都是用一个字节表示，而汉字使用三个字节。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">注意，虽然说utf是为了使用更少的空间而使用的，但那只是相对于unicode编码来说，如果已经知道是汉字，则使用GB2312/GBK无疑是最节省的。不过另一方面，值得说明的是，虽然utf编码对汉字使用3个字节，但即使对于汉字网页，utf编码也会比unicode编码节省，因为网页中包含了很多的英文字符。</p>
<p class="1">3. java对字符的处理</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">在java应用软件中，会有多处涉及到字符集编码，有些地方需要进行正确的设置，有些地方需要进行一定程度的处理。</p>
<p class="2">3.1. getBytes(charset)</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">这是java字符串处理的一个标准函数，其作用是将字符串所表示的字符按照charset编码，并以字节方式表示。注意字符串在java内存中总是按unicode编码存储的。比如"中文"，正常情况下（即没有错误的时候）存储为"<u>4e2d 6587</u>"，如果charset为"gbk"，则被编码为"<u>d6d0 cec4</u>"，然后返回字节"<u>d6 d0 ce c4</u>"。如果charset为"utf8"则最后是"<u>e4 b8 ad e6 96 87</u>"。如果是"iso8859-1"，则由于无法编码，最后返回 "<u>3f 3f</u>"（两个问号）。</p>
<p class="2">3.2. new String(charset)</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">这是java字符串处理的另一个标准函数，和上一个函数的作用相反，将字节数组按照charset编码进行组合识别，最后转换为unicode存储。参考上述getBytes的例子，"gbk" 和"utf8"都可以得出正确的结果"<u>4e2d 6587</u>"，但iso8859-1最后变成了"<u>003f 003f</u>"（两个问号）。</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">因为utf8可以用来表示/编码所有字符，所以new String( str.getBytes( "utf8" ), "utf8" ) === str，即完全可逆。</p>
<p class="2">3.3. setCharacterEncoding()</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">该函数用来设置http请求或者相应的编码。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">对于request，是指提交内容的编码，指定后可以通过getParameter()则直接获得正确的字符串，如果不指定，则默认使用iso8859-1编码，需要进一步处理。参见下述"表单输入"。值得注意的是在执行setCharacterEncoding()之前，不能执行任何getParameter()。java doc上说明：This method must be called prior to reading request parameters or reading input using getReader()。而且，该指定只对POST方法有效，对GET方法无效。分析原因，应该是在执行第一个getParameter()的时候，java将会按照编码分析所有的提交内容，而后续的getParameter()不再进行分析，所以setCharacterEncoding()无效。而对于GET方法提交表单是，提交的内容在URL中，一开始就已经按照编码分析所有的提交内容，setCharacterEncoding()自然就无效。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">对于response，则是指定输出内容的编码，同时，该设置会传递给浏览器，告诉浏览器输出内容所采用的编码。</p>
<p class="2">3.4. 处理过程</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">下面分析两个有代表性的例子，说明java对编码有关问题的处理方法。</p>
<p class="3">3.4.1. 表单输入</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">User input<u>&nbsp; *(gbk:d6d0 cec4)&nbsp; </u>browser<u>&nbsp; *(gbk:d6d0 cec4)&nbsp; </u>web server<u>&nbsp; iso8859-1(00d6 00d 000ce 00c4)&nbsp; </u>class，需要在class中进行处理：getbytes("iso8859-1")为<u>d6 d0 ce c4</u>，new String("gbk")为<u>d6d0 cec4</u>，内存中以unicode编码则为<u>4e2d 6587</u>。</p>
<p class="MsoBodyTextIndent" style="margin-left: 43pt; text-indent: -21pt; tab-stops: list 43.0pt"><span lang="EN-US" style="font-size: 11pt; font-family: Wingdings">l</span> 用户输入的编码方式和页面指定的编码有关，也和用户的操作系统有关，所以是不确定的，上例以gbk为例。</p>
<p class="MsoBodyTextIndent" style="margin-left: 43pt; text-indent: -21pt; tab-stops: list 43.0pt"><span lang="EN-US" style="font-size: 11pt; font-family: Wingdings">l</span> 从browser到web server，可以在表单中指定提交内容时使用的字符集，否则会使用页面指定的编码。而如果在url中直接用?的方式输入参数，则其编码往往是操作系统本身的编码，因为这时和页面无关。上述仍旧以gbk编码为例。</p>
<p class="MsoBodyTextIndent" style="margin-left: 43pt; text-indent: -21pt; tab-stops: list 43.0pt"><span lang="EN-US" style="font-size: 11pt; font-family: Wingdings">l</span> Web server接收到的是字节流，默认时（getParameter）会以iso8859-1编码处理之，结果是不正确的，所以需要进行处理。但如果预先设置了编码（通过request. setCharacterEncoding ()），则能够直接获取到正确的结果。</p>
<p class="MsoBodyTextIndent" style="margin-left: 43pt; text-indent: -21pt; tab-stops: list 43.0pt"><span lang="EN-US" style="font-size: 11pt; font-family: Wingdings">l</span> 在页面中指定编码是个好习惯，否则可能失去控制，无法指定正确的编码。</p>
<p class="3">3.4.2. 文件编译</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">假设文件是gbk编码保存的，而编译有两种编码选择：gbk或者iso8859-1，前者是中文windows的默认编码，后者是linux的默认编码，当然也可以在编译时指定编码。</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">Jsp<u>&nbsp; *(gbk:d6d0 cec4)&nbsp; </u>java file<u>&nbsp; *(gbk:d6d0 cec4)&nbsp; </u>compiler read<u>&nbsp; uincode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4)&nbsp; </u>compiler write<u>&nbsp; utf(gbk: e4b8ad e69687; iso8859-1: *)&nbsp; </u>compiled file<u>&nbsp; unicode(gbk: 4e2d 6587; iso8859-1: 00d6 00d 000ce 00c4)&nbsp; </u>class。所以用gbk编码保存，而用iso8859-1编译的结果是不正确的。</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">class<u>&nbsp; unicode(4e2d 6587)&nbsp; </u>system.out / jsp.out<u>&nbsp; gbk(d6d0 cec4)&nbsp; </u>os console / browser。</p>
<p class="MsoBodyTextIndent" style="margin-left: 43pt; text-indent: -21pt; tab-stops: list 43.0pt"><span lang="EN-US" style="font-size: 11pt; font-family: Wingdings">l</span> 文件可以以多种编码方式保存，中文windows下，默认为ansi/gbk。</p>
<p class="MsoBodyTextIndent" style="margin-left: 43pt; text-indent: -21pt; tab-stops: list 43.0pt"><span lang="EN-US" style="font-size: 11pt; font-family: Wingdings">l</span> 编译器读取文件时，需要得到文件的编码，如果未指定，则使用系统默认编码。一般class文件，是以系统默认编码保存的，所以编译不会出问题，但对于jsp文件，如果在中文windows下编辑保存，而部署在英文linux下运行/编译，则会出现问题。所以需要在jsp文件中用pageEncoding指定编码。</p>
<p class="MsoBodyTextIndent" style="margin-left: 43pt; text-indent: -21pt; tab-stops: list 43.0pt"><span lang="EN-US" style="font-size: 11pt; font-family: Wingdings">l</span> Java编译的时候会转换成统一的unicode编码处理，最后保存的时候再转换为utf编码。</p>
<p class="MsoBodyTextIndent" style="margin-left: 43pt; text-indent: -21pt; tab-stops: list 43.0pt"><span lang="EN-US" style="font-size: 11pt; font-family: Wingdings">l</span> 当系统输出字符的时候，会按指定编码输出，对于中文windows下，System.out将使用gbk编码，而对于response（浏览器），则使用jsp文件头指定的contentType，或者可以直接为response指定编码。同时，会告诉browser网页的编码。如果未指定，则会使用iso8859-1编码。对于中文，应该为browser指定输出字符串的编码。</p>
<p class="MsoBodyTextIndent" style="margin-left: 43pt; text-indent: -21pt; tab-stops: list 43.0pt"><span lang="EN-US" style="font-size: 11pt; font-family: Wingdings">l</span> browser显示网页的时候，首先使用response中指定的编码（jsp文件头指定的contentType最终也反映在response上），如果未指定，则会使用网页中meta项指定中的contentType。</p>
<p class="2">3.5. 几处设置</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">对于web应用程序，和编码有关的设置或者函数如下。</p>
<p class="3">3.5.1. jsp编译</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">指定文件的存储编码，很明显，该设置应该置于文件的开头。例如：&lt;%@page pageEncoding="GBK"%&gt;。另外，对于一般class文件，可以在编译的时候指定编码。</p>
<p class="3">3.5.2. jsp输出</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">指定文件输出到browser是使用的编码，该设置也应该置于文件的开头。例如：&lt;%@ page contentType="text/html; charset= GBK" %&gt;。该设置和response.setCharacterEncoding("GBK")等效。</p>
<p class="3">3.5.3. meta设置</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">指定网页使用的编码，该设置对静态网页尤其有作用。因为静态网页无法采用jsp的设置，而且也无法执行response.setCharacterEncoding()。例如：&lt;<ST1:PLACE w:st="on">META http-equiv="Content-Type" content="text/html; charset=GBK" /&gt;</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">如果同时采用了jsp输出和meta设置两种编码指定方式，则jsp指定的优先。因为jsp指定的直接体现在response中。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">需要注意的是，apache有一个设置可以给无编码指定的网页指定编码，该指定等同于jsp的编码指定方式，所以会覆盖静态网页中的meta指定。所以有人建议关闭该设置。</p>
<p class="3">3.5.4. form设置</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">当浏览器提交表单的时候，可以指定相应的编码。例如：&lt;form accept-charset= "gb2312"&gt;。一般不必不使用该设置，浏览器会直接使用网页的编码。</p>
<p class="1">4. 系统软件</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">下面讨论几个相关的系统软件。</p>
<p class="2">4.1. mysql数据库</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">很明显，要支持多语言，应该将数据库的编码设置成utf或者unicode，而utf更适合与存储。但是，如果中文数据中包含的英文字母很少，其实unicode更为适合。</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">数据库的编码可以通过mysql的配置文件设置，例如default-character-set=utf8。还可以在数据库链接URL中设置，例如： useUnicode=true&amp;characterEncoding=UTF-8。注意这两者应该保持一致，在新的sql版本里，在数据库链接URL里可以不进行设置，但也不能是错误的设置。</p>
<p class="2">4.2. apache</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">appache和编码有关的配置在httpd.conf中，例如AddDefaultCharset UTF-8。如前所述，该功能会将所有静态页面的编码设置为UTF-8，最好关闭该功能。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">另外，apache还有单独的模块来处理网页响应头，其中也可能对编码进行设置。</p>
<p class="2">4.3. linux默认编码</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">这里所说的linux默认编码，是指运行时的环境变量。两个重要的环境变量是LC_ALL和LANG，默认编码会影响到java URLEncode的行为，下面有描述。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">建议都设置为"zh_CN.UTF-8"。</p>
<p class="2">4.4. 其它</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">为了支持中文文件名，linux在加载磁盘时应该指定字符集，例如：mount /dev/hda5 /mnt/hda5/ -t ntfs -o iocharset=gb2312。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">另外，如前所述，使用GET方法提交的信息不支持request.setCharacterEncoding()，但可以通过tomcat的配置文件指定字符集，在tomcat的server.xml文件中，形如：&lt;Connector ... URIEncoding="GBK"/&gt;。这种方法将统一设置所有请求，而不能针对具体页面进行设置，也不一定和browser使用的编码相同，所以有时候并不是所期望的。</p>
<p class="1">5. URL地址</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">URL地址中含有中文字符是很麻烦的，前面描述过使用GET方法提交表单的情况，使用GET方法时，参数就是包含在URL中。</p>
<p class="2">5.1. URL编码</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">对于URL中的一些特殊字符，浏览器会自动进行编码。这些字符除了"/?&amp;"等外，还包括unicode字符，比如汉子。这时的编码比较特殊。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">IE有一个选项"总是使用UTF-8发送URL"，当该选项有效时，IE将会对特殊字符进行UTF-8编码，同时进行URL编码。如果改选项无效，则使用默认编码"GBK"，并且不进行URL编码。但是，对于URL后面的参数，则总是不进行编码，相当于UTF-8选项无效。比如"中文.html?a=中文"，当UTF-8选项有效时，将发送链接"%<u>e4%b8%ad%e6%96%87.html?a=\x4e\x2d\x65\x87</u>"；而UTF-8选项无效时，将发送链接"<u>\x4e\x2d\x65\x87.html?a=\x4e\x2d\x65\x87</u>"。注意后者前面的"中文"两个字只有4个字节，而前者却有18个字节，这主要时URL编码的原因。</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">当web server（tomcat）接收到该链接时，将会进行URL解码，即去掉"%"，同时按照ISO8859-1编码（上面已经描述，可以使用URLEncoding来设置成其它编码）识别。上述例子的结果分别是"<u>\ue4\ub8\uad\ue6\u96\u87.html?a=\u4e\u2d\u65\u87</u>"和"<u>\u4e\u2d\u65\u87.html?a=\u4e\u2d\u65\u87</u>"，注意前者前面的"中文"两个字恢复成了6个字符。这里用"\u"，表示是unicode。</p>
<p class="MsoBodyTextIndent" style="text-indent: 22pt">所以，由于客户端设置的不同，相同的链接，在服务器上得到了不同结果。这个问题不少人都遇到，却没有很好的解决办法。所以有的网站会建议用户尝试关闭UTF-8选项。不过，下面会描述一个更好的处理办法。</p>
<p class="2">5.2. rewrite</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">熟悉的人都知道，apache有一个功能强大的rewrite模块，这里不描述其功能。需要说明的是该模块会自动将URL解码（去除%），即完成上述web server（tomcat）的部分功能。有相关文档介绍说可以使用[NE]参数来关闭该功能，但我试验并未成功，可能是因为版本（我使用的是apache <ST1:CHSDATE w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year="1899">2.0.54）问题。另外，当参数中含有"?&amp; "等符号的时候，该功能将导致系统得不到正常结果。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">rewrite本身似乎完全是采用字节处理的方式，而不考虑字符串的编码，所以不会带来编码问题。</p>
<p class="2">5.3. URLEncode.encode()</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">这是Java本身提供对的URL编码函数，完成的工作和上述UTF-8选项有效时浏览器所做的工作相似。值得说明的是，java已经不赞成不指定编码来使用该方法（deprecated）。应该在使用的时候增加编码指定。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">当不指定编码的时候，该方法使用系统默认编码，这会导致软件运行结果得不确定。比如对于"中文"，当系统默认编码为"gb2312"时，结果是"%<u>4e%2d%65%87</u>"，而默认编码为"UTF-8"，结果却是"%<u>e4%b8%ad%e6%96%87</u>"，后续程序将难以处理。另外，这儿说的系统默认编码是由运行tomcat时的环境变量LC_ALL和LANG等决定的，曾经出现过tomcat重启后就出现乱码的问题，最后才郁闷的发现是因为修改修改了这两个环境变量。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">建议统一指定为"UTF-8"编码，可能需要修改相应的程序。</p>
<p class="2">5.4. 一个解决方案</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">上面说起过，因为浏览器设置的不同，对于同一个链接，web server收到的是不同内容，而软件系统有无法知道这中间的区别，所以这一协议目前还存在缺陷。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">针对具体问题，不应该侥幸认为所有客户的IE设置都是UTF-8有效的，也不应该粗暴的建议用户修改IE设置，要知道，用户不可能去记住每一个web server的设置。所以，接下来的解决办法就只能是让自己的程序多一点智能：根据内容来分析编码是否UTF-8。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">比较幸运的是UTF-8编码相当有规律，所以可以通过分析传输过来的链接内容，来判断是否是正确的UTF-8字符，如果是，则以UTF-8处理之，如果不是，则使用客户默认编码（比如"GBK"），下面是一个判断是否UTF-8的例子，如果你了解相应规律，就容易理解。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">public static boolean isValidUtf8(byte[] b,int aMaxCount){</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int lLen=b.length,lCharCount=0;</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i=0;i&lt;lLen &amp;&amp; lCharCount&lt;aMaxCount;++lCharCount){</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte lByte=b[i++];//to fast operation, ++ now, ready for the following for(;;)</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(lByte&gt;=0) continue;//&gt;=0 is normal ascii</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(lByte&lt;(byte)0xc0 || lByte&gt;(byte)0xfd) return false;</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int lCount=lByte&gt;(byte)0xfc?5:lByte&gt;(byte)0xf8?4</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; :lByte&gt;(byte)0xf0?3:lByte&gt;(byte)0xe0?2:1;</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(i+lCount&gt;lLen) return false;</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int j=0;j&lt;lCount;++j,++i) if(b[i]&gt;=(byte)0xc0) return false;</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true;</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">}</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">相应地，一个使用上述方法的例子如下：</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">public static String getUrlParam(String aStr,String aDefaultCharset)</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">throws UnsupportedEncodingException{</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(aStr==null) return null;</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte[] lBytes=aStr.getBytes("ISO-8859-1");</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new String(lBytes,StringUtil.isValidUtf8(lBytes)?"utf8":aDefaultCharset);</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">}</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">不过，该方法也存在缺陷，如下两方面：</p>
<p class="MsoBodyTextIndent" style="margin-left: 42pt; text-indent: -21pt; tab-stops: list 42.0pt"><span lang="EN-US" style="font-family: Wingdings">l</span> 没有包括对用户默认编码的识别，这可以根据请求信息的语言来判断，但不一定正确，因为我们有时候也会输入一些韩文，或者其他文字。</p>
<p class="MsoBodyTextIndent" style="margin-left: 42pt; text-indent: -21pt; tab-stops: list 42.0pt"><span lang="EN-US" style="font-family: Wingdings">l</span> 可能会错误判断UTF-8字符，一个例子是"学习"两个字，其GBK编码是" <u>\xd1\xa7\xcf\xb0</u>"，如果使用上述isValidUtf8方法判断，将返回true。可以考虑使用更严格的判断方法，不过估计效果不大。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">有一个例子可以证明google也遇到了上述问题，而且也采用了和上述相似的处理方法，比如，如果在地址栏中输入"<a href="http://www.google.com/search?hl=zh-CN&amp;newwindow=1&amp;q=学习">http://www.google.com/search?hl=zh-CN&amp;newwindow=1&amp;q=学习</a>"，google将无法正确识别，而其他汉字一般能够正常识别。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">最后，应该补充说明一下，如果不使用rewrite规则，或者通过表单提交数据，其实并不一定会遇到上述问题，因为这时可以在提交数据时指定希望的编码。另外，中文文件名确实会带来问题，应该谨慎使用。</p>
<p class="1">6. 其它</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">下面描述一些和编码有关的其他问题。</p>
<p class="2">6.1. SecureCRT</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">除了浏览器和控制台与编码有关外，一些客户端也很有关系。比如在使用SecureCRT连接linux时，应该让SecureCRT的显示编码（不同的session，可以有不同的编码设置）和linux的编码环境变量保持一致。否则看到的一些帮助信息，就可能是乱码。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">另外，mysql有自己的编码设置，也应该保持和SecureCRT的显示编码一致。否则通过SecureCRT执行sql语句的时候，可能无法处理中文字符，查询结果也会出现乱码。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">对于Utf-8文件，很多编辑器（比如记事本）会在文件开头增加三个不可见的标志字节，如果作为mysql的输入文件，则必须要去掉这三个字符。（用linux的vi保存可以去掉这三个字符）。一个有趣的现象是，在中文windows下，创建一个新txt文件，用记事本打开，输入"连通"两个字，保存，再打开，你会发现两个字没了，只留下一个小黑点。</p>
<p class="2">6.2. 过滤器</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">如果需要统一设置编码，则通过filter进行设置是个不错的选择。在filter class中，可以统一为需要的请求或者回应设置编码。参加上述setCharacterEncoding()。这个类apache已经给出了可以直接使用的例子SetCharacterEncodingFilter。</p>
<p class="2">6.3. POST和GET</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">很明显，以POST提交信息时，URL有更好的可读性，而且可以方便的使用setCharacterEncoding()来处理字符集问题。但GET方法形成的URL能够更容易表达网页的实际内容，也能够用于收藏。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">从统一的角度考虑问题，建议采用GET方法，这要求在程序中获得参数是进行特殊处理，而无法使用setCharacterEncoding()的便利，如果不考虑rewrite，就不存在IE的UTF-8问题，可以考虑通过设置URIEncoding来方便获取URL中的参数。</p>
<p class="2">6.4. 简繁体编码转换</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">GBK同时包含简体和繁体编码，也就是说同一个字，由于编码不同，在GBK编码下属于两个字。有时候，为了正确取得完整的结果，应该将繁体和简体进行统一。可以考虑将UTF、GBK中的所有繁体字，转换为相应的简体字，BIG5编码的数据，也应该转化成相应的简体字。当然，仍旧以UTF编码存储。</p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt">例如，对于"语言 語言"，用UTF表示为"<u>\x</u><u>E8\xAF\xAD\xE8\xA8\x80 \xE8\xAA\x9E\xE8\xA8\x80</u>"，进行简繁体编码转换后应该是两个相同的 "<u>\x</u><u>E8\xAF\xAD\xE8\xA8\x80&gt;</u>"。<br />
<br />
<br />
&nbsp;转载于：刘科垠 <a href="http://china.eceel.com/">Manufacturer.com</a> </p>
<p class="MsoBodyTextIndent" style="text-indent: 21pt; text-align: right" align="right"></ST1:CHSDATE></ST1:PLACE>&nbsp;</p>
<img src ="http://www.blogjava.net/ArcticOcean/aggbug/324841.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ArcticOcean/" target="_blank">Cool Jazz</a> 2010-06-30 09:08 <a href="http://www.blogjava.net/ArcticOcean/articles/324841.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>邮件简历照片显示，测试成功（在Blogjava）</title><link>http://www.blogjava.net/ArcticOcean/archive/2008/11/09/239543.html</link><dc:creator>Cool Jazz</dc:creator><author>Cool Jazz</author><pubDate>Sun, 09 Nov 2008 12:01:00 GMT</pubDate><guid>http://www.blogjava.net/ArcticOcean/archive/2008/11/09/239543.html</guid><wfw:comment>http://www.blogjava.net/ArcticOcean/comments/239543.html</wfw:comment><comments>http://www.blogjava.net/ArcticOcean/archive/2008/11/09/239543.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ArcticOcean/comments/commentRss/239543.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ArcticOcean/services/trackbacks/239543.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最近比较忙。因为我马上就要毕业了，正在找工作。自己去网上下了html版的简历。编辑了，放入邮箱。在自己的机子上能看出来，我以为就发出去别人也能看得到呢。后来我同学问我怎么才能在邮件里显示照片，我就把方法告诉他，并且把他的照片传到了我在JR的相册里。起初在他的邮件里（本地网络）可以看到。我就以为成功了呢。改天他发了邮件给他同学看，却发现看不到。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我真是奇怪了。怎么会看不到呢？<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 照片的地址，单独点击的话可以看到。在别人的邮件里就是看不到，除非那个人也点击了照片的地址，并把照片显示出来后，才能在邮件里显示。搞了好几次都是这个样子，我无语了。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 难道说JR的相册不能被外人看到？<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不应该啊！别人是可以看到的呀。莫非JR的相册自动限制了，照片的下载？<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;也许是这个原因，我想。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我换了Blogjava，找了我的同学测试。把JR的测试了3，4次都不能显示。最后我测试了Blogjava，成功了！（方法就不用我在啰嗦了吧：））<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看来还是Blogjava好啊。我后一定要在这里多写blog！<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以前，我很少写技术的blog，因为自己想写的东西多数和公司的代码有关，有时候想写却不能把代码贴出来。索性就不写blog。只是去技术性的网站或开源的网站看最新的英文新闻。如果有新消息，就把它翻译过来，放到自己的blog里。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Blogjava给我的感觉是，功能比JR的强。但是人气似乎要弱一些。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 不关怎么样，我以后会多到这里来的。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<img src ="http://www.blogjava.net/ArcticOcean/aggbug/239543.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ArcticOcean/" target="_blank">Cool Jazz</a> 2008-11-09 20:01 <a href="http://www.blogjava.net/ArcticOcean/archive/2008/11/09/239543.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>好多天没来了，因为在写论文</title><link>http://www.blogjava.net/ArcticOcean/archive/2008/09/21/230255.html</link><dc:creator>Cool Jazz</dc:creator><author>Cool Jazz</author><pubDate>Sun, 21 Sep 2008 05:46:00 GMT</pubDate><guid>http://www.blogjava.net/ArcticOcean/archive/2008/09/21/230255.html</guid><wfw:comment>http://www.blogjava.net/ArcticOcean/comments/230255.html</wfw:comment><comments>http://www.blogjava.net/ArcticOcean/archive/2008/09/21/230255.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ArcticOcean/comments/commentRss/230255.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ArcticOcean/services/trackbacks/230255.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 好多天没来了。最近在忙论文的事情，每天白天要上班。公司晚上又断线不能上网。所以每天只能查了资料保存起来晚上在看。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 毕竟以前做了数据库的项目，相对来说好写点。只是我做的那些东西被师兄给&#8220;写&#8221;了。所以留给自己的难度不小。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 哎，公司现在运行的东西比较老了，虽然有市场。多少年都是这样的老架构。没有新东西出来，将来会比较麻烦。现在是在公司，身不由己。既然公司项目帮不上什么忙，就自己动手了。采用现在的主流框架，结合IEC规约，在整合上数据库集群，服务器集群。。。似乎是多了，但是不写，自己会觉得没有什么东西。学校那些老师，都不知道现在外面的技术进展情况，就是那老技术来写，他们可能还觉得挺新的。不过，不能骗自己啊。搞这么多架构的东西，没有程序，没有实际的运行和测试，你能说服得了谁？程序是写了，把原来在学校研究的项目拿来，加上Spring改造，DWR包装成JavaScript，在传给测试界面。。。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 真难啊，最难的也就是调试了。也就是只能每天晚上搞，真累！<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 还好我搞出来了，放在Apache和Tomcat集群下，运行这些程序抓了些图，真爽啊！<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 只是我的服务器集群是网上参考别人的，还真没运行过。哎，没机器啊，公司的机器又不能随便用。我最怕的是通信，我没做过，只是用了公司以前做的通信组件拿来用，还行能用。要是连RTU测的话，就更好了，可惜。。。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 庆祝一下，我的服务器后台架构测试成功！感谢Java，感谢Spring和Hibernate，感谢PostgreSQL，感谢开源！<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 整理论文咯 ：）
  <img src ="http://www.blogjava.net/ArcticOcean/aggbug/230255.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ArcticOcean/" target="_blank">Cool Jazz</a> 2008-09-21 13:46 <a href="http://www.blogjava.net/ArcticOcean/archive/2008/09/21/230255.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>这么快，Hibernate3.3.1GA发布！</title><link>http://www.blogjava.net/ArcticOcean/archive/2008/09/12/228510.html</link><dc:creator>Cool Jazz</dc:creator><author>Cool Jazz</author><pubDate>Fri, 12 Sep 2008 01:50:00 GMT</pubDate><guid>http://www.blogjava.net/ArcticOcean/archive/2008/09/12/228510.html</guid><wfw:comment>http://www.blogjava.net/ArcticOcean/comments/228510.html</wfw:comment><comments>http://www.blogjava.net/ArcticOcean/archive/2008/09/12/228510.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ArcticOcean/comments/commentRss/228510.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ArcticOcean/services/trackbacks/228510.html</trackback:ping><description><![CDATA[<div style="color: #000000; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 12px; margin-top: 8px; margin-right: 8px; margin-bottom: 8px; margin-left: 8px; background-image: initial; background-repeat: initial; background-attachment: initial; background-color: #ffffff; background-position: initial initial; ">
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">&#160;&#160; &#160;在距离Hibernate3.3.0GA发布不到一个月的时间内，3.3.1GA发布了。感觉真是快啊。原来是修正了bug。</p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">&#160;&#160; &#160;这次发布主要是修正了<span  style="font-family: white-space: pre-wrap; ">CurrentSessionContext(SessionFactory.getCurrentSession())运行在IBM WebSphered的严重问题。<span  style="white-space: normal; ">它也包括了id发生器等不少改进。细节部分和全部变化，请查看变化日志。</span></span></p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">&#160;&#160; &#160;<span  style="font-family: 'Lucida Sans'; font-size: 13px; line-height: 19px; ">Hibernate Core 3.3.1 has just been&#160;<a href="https://sourceforge.net/project/showfiles.php?group_id=40712&amp;package_id=127784&amp;release_id=625684" target="" class="regularLink" style="color: #d75525; text-decoration: underline; ">released</a>&#160;with its artifacts published to the JBoss Maven&#160;<a href="http://repository.jboss.org/maven2/org/hibernate/" target="" class="regularLink" style="color: #d75525; text-decoration: underline; ">Repository</a>. This release fixed a serious issue with CurrentSessionContext (SessionFactory.getCurrentSession()) when running on IBM WebSphere. It also includes quite a few improvements to the&#160;<a href="http://in.relation.to/Bloggers/New323HibernateIdentifierGenerators" target="" class="regularLink" style="color: #d75525; text-decoration: underline; ">enhanced id generators</a>. For details about all the changes in this release, check out it's&#160;<a href="https://sourceforge.net/project/shownotes.php?release_id=625684&amp;group_id=40712" target="" class="regularLink" style="color: #d75525; text-decoration: underline; ">changelog</a></span></p>
<p style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; ">&#160;&#160;&#160;</p>
</div>
 <img src ="http://www.blogjava.net/ArcticOcean/aggbug/228510.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ArcticOcean/" target="_blank">Cool Jazz</a> 2008-09-12 09:50 <a href="http://www.blogjava.net/ArcticOcean/archive/2008/09/12/228510.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>新来的，安个家。</title><link>http://www.blogjava.net/ArcticOcean/archive/2008/09/11/228377.html</link><dc:creator>Cool Jazz</dc:creator><author>Cool Jazz</author><pubDate>Thu, 11 Sep 2008 08:07:00 GMT</pubDate><guid>http://www.blogjava.net/ArcticOcean/archive/2008/09/11/228377.html</guid><wfw:comment>http://www.blogjava.net/ArcticOcean/comments/228377.html</wfw:comment><comments>http://www.blogjava.net/ArcticOcean/archive/2008/09/11/228377.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ArcticOcean/comments/commentRss/228377.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ArcticOcean/services/trackbacks/228377.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp; 去的Blog不多，偶然见了Blogjava。没什么想法，安个家吧。也许这里会僻静些。<br />
&nbsp;&nbsp;&nbsp; 写个首Blog，发现这里的Blog方法提示还有英文，呵呵。不过，对于看API来说，这些算小意思。<br />
&nbsp;&nbsp;&nbsp; 乱涂，乱涂。。。<br />
<span lang="EN-US" style="font-size: 10pt; font-family: 'Courier New'; mso-fareast-font-family: 宋体; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><v:shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600">&nbsp;&nbsp;</v:shapetype></span><br />
&nbsp;&nbsp;&nbsp; </p>
 <img src ="http://www.blogjava.net/ArcticOcean/aggbug/228377.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ArcticOcean/" target="_blank">Cool Jazz</a> 2008-09-11 16:07 <a href="http://www.blogjava.net/ArcticOcean/archive/2008/09/11/228377.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>