在我们国家,按拼音进行排序是很常见的需求,比如姓名,那如何按拼音进行排序呢?
假如我们有以下List:
List<String> list = new ArrayList<>();
list.add("周瑜");
list.add("大都督");
list.add("周文王");
list.add("周武王");
list.add("周公");
如果我们采用最简单的排序方式:
list.sort(String::compareTo);
结果为:
[周公, 周文王, 周武王, 周瑜, 大都督]
很明显,结果不是拼音顺序,“大都督”应该要在最前面,那这种情况是按什么逻辑排的序呢?
上面的排序代码相当于:
list.sort(new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
本质上是比较两个String的大小,小的排在前面,大的排在后面,那两个String默认是怎么比较大小的呢?
通过查看compareTo的源码,发现底层调用的是:
StringUTF16.compareTo(s1, s2)
因此,本质上比较的是两个String的UTF16编码的大小关系,比如:
- “周瑜”的UTF16编码为:54 68 74 5C
- “大都督”的UTF16编码为:59 27 90 FD 77 63
很明显,直接比较第一个字节就能发现“周瑜”小于“大都督”,因为54小于59,所以在排序结果中,“周瑜”排在了“大都督”的前面。
原谅我,以上都是铺垫,接下来才是正文,那如何按拼音进行排序呢?
排序的本质其实都一样,都是把字符转成特定的编码,然后比较编码的大小关系,那么有没有一种编码是按拼音来编码的呢?
有,那就是GBK。
比如:
- “啊”的GKB编码为:B0 A1
- “阿”的GKB编码为:B0 A2
或者,大家可以直接去看GBK的编码表:GBK编码表
我截其中一部分
可以发现,GBK是妥妥的按拼音顺序来的。
因此,我们只需要将字符串按GBK进行编码然后比较大小,就可以实现按拼音排序了,比如:
list.sort(new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
try {
byte[] s1Bytes = s1.getBytes("GBK");
byte[] s2Bytes = s2.getBytes("GBK");
int s1Size = s1Bytes.length;
int s2Size = s2Bytes.length;
int size = Math.min(s1Size, s2Size);
for (int i = 0; i < size; i++) {
byte b1 = s1Bytes[i];
byte b2 = s2Bytes[i];
if (b1 != b2) {
return b1 - b2;
}
}
return s1Size - s2Size;
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
});
这样,结果就变为了:
[大都督, 周公, 周文王, 周武王, 周瑜]
符合预期。
当然,上面的代码写的比较粗糙,Java中有现成的实现,比如:
list.sort(new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return Collator.getInstance(Locale.CHINESE).compare(s1, s2);
}
});
结果也是:
[大都督, 周公, 周文王, 周武王, 周瑜]
或者你可以直接用Hutool提供的封装好的方法:
CollUtil.sortByPinyin(list);
本质都是一样的,都是按GBK来进行编码和排序的。
这就是拼音排序,感谢大家的点赞、关注、分享,谢谢。
我是大都督周瑜,欢迎关注我的公众号:IT周瑜,我会持续分享深度技术文章、常见经典面试题、学习路线、职业规范、实战经验。