讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議59:對字符串排序持一種寬容的心態 >

建議59:對字符串排序持一種寬容的心態

在Java中一涉及中文處理就會冒出很多問題來,其中排序也是一個讓人頭疼的課題,我們來看下面的代碼:


public static void main(Stringargs){

Stringstrs={"張三(Z)","李四(L)","王五(W)"};

//排序,默認是升序

Arrays.sort(strs);

int i=0;

for(String str:strs){

System.out.println((++i)+"、"+str);

}

}


上面的代碼定義一個數組,然後進行升序排序,我們期望的結果是按照拼音升序排列,即為李四、王五、張三,但是結果卻不是這樣的:


1、張三(Z)

2、李四(L)

3、王五(W)


這是按照什麼排序的呀,非常混亂!我們知道Arrays工具類的默認排序是通過數組元素的compareTo方法來進行比較的,那我們來看String類的compareTo的主要實現:


while(k<lim){

//原字符串的字符數組

char c1=v1[k];

//比較字符串的字符數組

char c2=v2[k];

if(c1!=c2){

//比較兩者的char值大小

return c1-c2;

}

k++;

}


上面的代碼先取得字符串的字符數組,然後一個一個地比較大小,注意這裡是字符比較(減號操作符),也就是UNICODE碼值的比較,查一下UNICODE代碼表,「張」的碼值是5F20,而「李」是674E,這樣一看,「張」排在「李」的前面也就很正確了——但這明顯與我們的意圖衝突了。這一點在JDK文檔中也有說明:對於非英文的String排序可能會出現不準確的情況。那該如何解決這個問題呢?Java推薦使用Collator類進行排序,那好,我們把代碼修改一下:


public static void main(Stringargs)throws Exception{

Stringstrs={"張三(Z)","李四(L)","王五(W)"};

//定義一個中文排序器

Comparator c=Collator.getInstance(Locale.CHINA);

//升序排列

Arrays.sort(strs, c);

int i=0;

for(String str:strs){

System.out.println((++i)+"、"+str);

}

}


輸出結果如下:


1、李四(L)

2、王五(W)

3、張三(Z)


這確實是我們期望的結果,應該舉杯慶賀了吧!但是且慢,中國的漢字博大精深,Java是否都能精確的排序呢?最主要的一點是漢字中有象形文字,音形分離,是不是每個漢字都能按照拼音的順序排列好呢?我們寫一個複雜的漢字來看看:


public static void main(Stringargs)throws Exception{

Stringstrs={"奔(B)","鑫(X)"};

Arrays.sort(strs, Collator.getInstance(Locale.CHINA));

int i=0;

for(String str:strs){

System.out.println((++i)+"、"+str);

}

}


三個牛「奔」讀bēn,三個金「鑫」讀xīn,這兩個字經常出現在飯店和商店的名稱上,我們來看排序的輸出結果:


1、鑫(X)

2、奔(B)


輸出結果又亂了!不要責怪Java,它已經盡量為我們考慮了,只是因為我們的漢字文化太博大精深了,要做好這個排序確實有點難為它。更深層次的原因是Java使用的是UNICODE編碼,而中文UNICODE字符集是來源於GB18030的,GB18030又是從GB2312發展起來,GB2312是一個包含了7000多個字符的字符集,它是按照拼音排序,並且是連續的,之後的GBK、GB18030都是在其基礎上擴充出來的,所以要讓它們完整排序也就難上加難了。

如果是排序對象是經常使用的漢字,使用Collator類排序完全可以滿足我們的要求,畢竟GB2312已經包含了大部分的漢字,如果需要嚴格排序,則要使用一些開源項目來自己實現了,比如pinyin4j可以把漢字轉換為拼音,然後我們自己來實現排序算法,不過此時你也會發現要考慮諸如算法、同音字、多音字等眾多問題。

注意 如果排序不是一個關鍵算法,使用Collator類即可。