对于一个数组或者列表或者集合的元素进行排序是一个比较常用的需求。现有的Java类库也提供了API来实现这样的功能,比如Arrays.sort以及Collections.sort的方法。另外,我们也可以用Collator来实现中文的排序。
1. package
2.
3. import
4. import
5. import
6. import
7. import
8.
9. public class
10.
11. public static void
12.
13. new
14. "中");
15. "文");
16. "拼");
17. "音");
18. "鑫");
19. "犇");
20.
21. Collections.sort(list, Collator.getInstance(Locale.CHINA));
22.
23. for(String ele: list)
24. {
25. System.out.println(ele);
26. }
27.
28. }
29. }
输出的结果是:
拼
文
音
中
鑫
犇
中文排序后的结果,并不完全正确。
这是因为Java使用的是Unicode编码,而常用的中文编码是GB2312,它包含了7000个字符集而且是按照拼音排序的,也是连续的。GB18030和GBK都是在此基础上扩展起来的,这样就会造成Unicode的不连续性,最终导致对有些中文字符排序不完全正确的结果。
为了能够更好地对中文进行排序,我们可以采用拼音或者笔画来对中文进行排序。本文采用了拼音来排序。将汉语转换成拼音的开源项目有PinYin4j,下载的地址如下:
1. package
2.
3. import
4. import
5. import
6. import
7. import
8. import
9. import
10.
11. public final class
12.
13. private
14. }
15.
16. /**
17. * 判断一个字符是否是中文字符
18. */
19. private static boolean isChineseCharacter(char
20. return String.valueOf(c).matches("[\\u4E00-\\u9FA5]+");
21. }
22.
23. /**
24. * 将一个含有中文的字符串转换成拼音。
25. * Note: 这里只是将中文转换成拼音,其它的各种字符将保持原来的样子。
26. */
27. public static
28.
29. if (null
30. return null;
31. }
32.
33. new
34. char[] charArray = aChineseValue.toCharArray();
35.
36. new
37. outputFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
38. outputFormat.setVCharType(HanyuPinyinVCharType.WITH_V);
39. outputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
40.
41. for (int i = 0; i < charArray.length; i++) {
42. if
43. try
44. sb.append(PinyinHelper.toHanyuPinyinStringArray(
45. 0]);
46. catch
47. e.printStackTrace();
48. }
49. else
50. sb.append(charArray[i]);
51. }
52. }
53. return
54. }
55.
56. /**
57. * 将一个含有中文的字符串转换成拼音。
58. * Note: 这里只是将中文转换成拼音,其它的各种字符将保持原来的样子。
59. *
60. * 在這裡多了一個參數needToCorrectSpelling,这个主要用于调整姓氏的发音。
61. * 如 '单', 这个字作为姓氏念 shan, 但同时也有dan的发音等。
62. *
63. * 为了保证姓氏发音的正确性,将了这个参数和相关的简单逻辑
64. */
65. public static
66. boolean
67.
68. if (null
69. return null;
70. }
71.
72. new
73. char[] charArray = aChineseValue.toCharArray();
74.
75. new
76. outputFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);
77. outputFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
78. outputFormat.setVCharType(HanyuPinyinVCharType.WITH_V);
79.
80. null;
81. for (int i = 0; i < charArray.length; i++) {
82. if
83. try
84. if (needToCorrectSpelling && 0
85. surname = SurnameDictionary
86. .populateCorrectSpelling(charArray[i]);
87. if (null
88. sb.append(PinyinHelper.toHanyuPinyinStringArray(
89. 0]);
90. else
91. sb.append(surname);
92. }
93. else
94. sb.append(PinyinHelper.toHanyuPinyinStringArray(
95. 0]);
96. }
97.
98. catch
99. e.printStackTrace();
100. }
101. else
102. sb.append(charArray[i]);
103. }
104. }
105. return
106. }
107.
108. public static void main(String[] args) throws
109. "拼音4j%^**Cool12568 カ キ ";
110. System.out.println(populatePinYing(x));
111. }
112. }
本文的一个简单例子是对名字进行排序,所以下面创建两个类 User 和 NameComparator
1. package
2.
3. import
4.
5. public class User implements
6.
7. private static final long
8.
9. private
10.
11. private int
12.
13. /**
14. * @param name
15. * @param age
16. */
17. public User(String name, int
18. this.name = name;
19. this.age = age;
20. }
21.
22. public
23.
24. }
25.
26. /**
27. * @return the name
28. */
29. public
30. return
31. }
32.
33. /**
34. * @param name
35. * the name to set
36. */
37. public void
38. this.name = name;
39. }
40.
41. /**
42. * @return the age
43. */
44. public int
45. return
46. }
47.
48. /**
49. * @param age
50. * the age to set
51. */
52. public void setAge(int
53. this.age = age;
54. }
55.
56. }
1. package
2.
3. import
4.
5. public class NameComparator implements
6.
7. public int
8.
9. String name1 = u1.getName();
10. String name2 = u2.getName();
11.
12. return
13. PinYinUtils.populatePinYing(name2));
14. }
15.
16. }
测试代码如下:
Before sorting.....
1. package
2.
3. import
4. import
5. import
6.
7. public class
8.
9. public static void
10.
11. run();
12. }
13.
14. private static void
15. List<User> list = prepareTestUserList();
16.
17. printListBeforeSorting(list);
18.
19. new
20.
21. printListAfteSorting(list);
22. }
23.
24. private static void
25. "After sorting.....");
26. printList(list);
27. }
28.
29. private static void
30. "Before sorting.....");
31. printList(list);
32. }
33.
34. private static void
35. for
36. System.out.println(user.getName());
37. }
38. }
39.
40. private static
41. new
42. new
43. "张三");
44. 21);
45. list.add(u);
46.
47. new
48. "李四");
49. 18);
50. list.add(u);
51.
52. new
53. "王五");
54. 25);
55. list.add(u);
56.
57. new
58. "鑫鑫");
59. 89);
60. list.add(u);
61.
62. new
63. "范范");
64. 89);
65. list.add(u);
66.
67. new
68. "单一号");
69. 89);
70. list.add(u);
71.
72. new
73. "犇犇");
74. 89);
75. list.add(u);
76.
77. return
78. }
79.
80. }
张三
李四
王五
鑫鑫
范范
单一号
犇犇
After sorting.....
犇犇
单一号
范范
李四
王五
鑫鑫
张三
从上述的排序结果,可以看出“单一号”并没有在一个准确的位置上。产生这种情况是因为中文存在多音字,而且姓氏中的发音会不一样。如“单”在姓氏中需要发“shan”,用PinYin4j能够返回多音字,因为不知道哪一个正确,我在代码中返回了第一个 “dan”。
为了能够保证姓氏发声的正确性,个人想到的有三种:
1. 数据库中存储姓氏发音的表
2. 将姓氏的发音存储在一个key-value的properties文件中
3. 创建一个姓氏发音的字典类
本文简单采用第三种方式来实现:
1. package
2.
3.
4. public class
5.
6. public static String populateCorrectSpelling(char
7.
8. //TODO:
9. if ('单'
10. return "shan";
11. }
12.
13. return null;
14. }
15.
16. }
比较器中加入参数,将SurnameDictionary运用上去。
1. package
2.
3. import
4.
5. public class NameComparator implements
6.
7. public int
8.
9. String name1 = u1.getName();
10. String name2 = u2.getName();
11.
12. return PinYinUtils.populatePinYing(name1, true).compareTo(
13. true));
14. }
15.
16. }
重新运行结果,我们将得到正确的名字排序结果:
Before sorting.....
张三
李四
王五
鑫鑫
范范
单一号
犇犇
After sorting.....
犇犇
范范
李四
单一号
王五
鑫鑫
张三