对于一个数组或者列表或者集合的元素进行排序是一个比较常用的需求。现有的Java类库也提供了API来实现这样的功能,比如Arrays.sort以及Collections.sort的方法。另外,我们也可以用Collator来实现中文的排序。
class="java">package demo;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
public class TestCollator {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("中");
list.add("文");
list.add("拼");
list.add("音");
list.add("鑫");
list.add("犇");
Collections.sort(list, Collator.getInstance(Locale.CHINA));
for(String ele: list)
{
System.out.println(ele);
}
}
}
输出的结果是:
拼
文
音
中
鑫
犇
中文排序后的结果,并不完全正确。
这是因为Java使用的是Unicode
编码,而常用的中文编码是GB2312,它包含了7000个字符集而且是按照拼音排序的,也是连续的。GB18030和GBK都是在此基础上扩展起来的,这样就会造成Unicode的不连续性,最终导致对有些中文字符排序不完全正确的结果。
为了能够更好地对中文进行排序,我们可以采用拼音或者笔画来对中文进行排序。本文采用了拼音来排序。将汉语转换成拼音的
开源项目有PinYin4j,下载的地址如下:
http://sourceforge.net/
projects/pinyin4j/
files/pinyin4j-2.5.0/pinyin4j-2.5.0/pinyin4j-2.5.0.zip/download
编写汉字转换成拼音的工具类
package demo;
import java.io.UnsupportedEncodingException;
import net.sourceforge.pinyin4j.PinyinHelper;
import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
public final class PinYinUtils {
private PinYinUtils() {
}
/**
* 判断一个字符是否是中文字符
*/
private static boolean isChineseCharacter(char c) {
return String.valueOf(c).matches("[\\u4E00-\\u9FA5]+");
}
/**
* 将一个含有中文的字符串转换成拼音。
* Note: 这里只是将中文转换成拼音,其它的各种字符将保持原来的样子。
*/
public static String populatePinYing(String aChineseValue) {
if (null == aChineseValue) {
return null;
}
StringBuilder sb = new StringBuilder();
char[] charArray = aChineseValue.toCharArray();
HanyuPinyinOutputFormat outputFormat = new HanyuPinyinOutputFormat();
outputFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
outputFormat.setVCharType(HanyuPinyinVCharType.WITH_V);
outputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
for (int i = 0; i < charArray.length; i++) {
if (isChineseCharacter(charArray[i])) {
try {
sb.append(PinyinHelper.toHanyuPinyinStringArray(
charArray[i], outputFormat)[0]);
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
sb.append(charArray[i]);
}
}
return sb.toString();
}
/**
* 将一个含有中文的字符串转换成拼音。
* Note: 这里只是将中文转换成拼音,其它的各种字符将保持原来的样子。
*
* 在這裡多了一個參數needToCorrectSpelling,这个主要用于调整姓氏的发音。
* 如 '单', 这个字作为姓氏念 shan, 但同时也有dan的发音等。
*
* 为了保证姓氏发音的正确性,将了这个参数和相关的简单逻辑
*/
public static String populatePinYing(String aChineseValue,
boolean needToCorrectSpelling) {
if (null == aChineseValue) {
return null;
}
StringBuilder sb = new StringBuilder();
char[] charArray = aChineseValue.toCharArray();
HanyuPinyinOutputFormat outputFormat = new HanyuPinyinOutputFormat();
outputFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
outputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
outputFormat.setVCharType(HanyuPinyinVCharType.WITH_V);
String surname = null;
for (int i = 0; i < charArray.length; i++) {
if (isChineseCharacter(charArray[i])) {
try {
if (needToCorrectSpelling && 0 == i) {
surname = SurnameDictionary
.populateCorrectSpelling(charArray[i]);
if (null == surname) {
sb.append(PinyinHelper.toHanyuPinyinStringArray(
charArray[i], outputFormat)[0]);
} else {
sb.append(surname);
}
} else {
sb.append(PinyinHelper.toHanyuPinyinStringArray(
charArray[i], outputFormat)[0]);
}
} catch (BadHanyuPinyinOutputFormatCombination e) {
e.printStackTrace();
}
} else {
sb.append(charArray[i]);
}
}
return sb.toString();
}
public static void main(String[] args) throws UnsupportedEncodingException {
String x = "拼音4j%^**Cool12568 ? ? ";
System.out.println(populatePinYing(x));
}
}
本文的一个简单
例子是对名字进行排序,所以下面创建两个类 User 和 NameComparator
package demo;
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = -1221268239932915488L;
private String name;
private int age;
/**
* @param name
* @param age
*/
public User(String name, int age) {
this.name = name;
this.age = age;
}
public User() {
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name
* the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the age
*/
public int getAge() {
return age;
}
/**
* @param age
* the age to set
*/
public void setAge(int age) {
this.age = age;
}
}
package demo;
import java.util.Comparator;
public class NameComparator implements Comparator<User> {
public int compare(User u1, User u2) {
String name1 = u1.getName();
String name2 = u2.getName();
return PinYinUtils.populatePinYing(name1).compareTo(
PinYinUtils.populatePinYing(name2));
}
}
测试代码如下:
package demo;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Test {
public static void main(String[] args) {
run();
}
private static void run() {
List<User> list = prepareTestUserList();
printListBeforeSorting(list);
Collections.sort(list, new NameComparator());
printListAfteSorting(list);
}
private static void printListAfteSorting(List<User> list) {
System.out.println("After sorting.....");
printList(list);
}
private static void printListBeforeSorting(List<User> list) {
System.out.println("Before sorting.....");
printList(list);
}
private static void printList(List<User> list) {
for (User user : list) {
System.out.println(user.getName());
}
}
private static List<User> prepareTestUserList() {
List<User> list = new ArrayList<User>();
User u = new User();
u.setName("张三");
u.setAge(21);
list.add(u);
u = new User();
u.setName("李四");
u.setAge(18);
list.add(u);
u = new User();
u.setName("王五");
u.setAge(25);
list.add(u);
u = new User();
u.setName("鑫鑫");
u.setAge(89);
list.add(u);
u = new User();
u.setName("范范");
u.setAge(89);
list.add(u);
u = new User();
u.setName("单一号");
u.setAge(89);
list.add(u);
u = new User();
u.setName("犇犇");
u.setAge(89);
list.add(u);
return list;
}
}
Before sorting.....
张三
李四
王五
鑫鑫
范范
单一号
犇犇
After sorting.....
犇犇
单一号
范范
李四
王五
鑫鑫
张三
从上述的排序结果,可以看出“单一号”并没有在一个准确的位置上。产生这种情况是因为中文存在多音字,而且姓氏中的发音会不一样。如“单”在姓氏中需要发“shan”,用PinYin4j能够返回多音字,因为不知道哪一个正确,我在代码中返回了第一个 “dan”。
为了能够保证姓氏发声的正确性,个人想到的有三种:1. 数据库中存储姓氏发音的表
2. 将姓氏的发音存储在一个key-value的properties文件中
3. 创建一个姓氏发音的字典类
本文简单采用第
三种方式来实现:
package demo;
public class SurnameDictionary {
public static String populateCorrectSpelling(char surname) {
//TODO:
if ('单' == surname) {
return "shan";
}
return null;
}
}
比较器中加入参数,将SurnameDictionary运用上去。
package demo;
import java.util.Comparator;
public class NameComparator implements Comparator<User> {
public int compare(User u1, User u2) {
String name1 = u1.getName();
String name2 = u2.getName();
return PinYinUtils.populatePinYing(name1, true).compareTo(
PinYinUtils.populatePinYing(name2, true));
}
}
重新运行结果,我们将得到正确的名字排序结果:
Before sorting.....
张三
李四
王五
鑫鑫
范范
单一号
犇犇
After sorting.....
犇犇
范范
李四
单一号
王五
鑫鑫
张三