目录
引言
基于pinyin4j依赖(net.sourceforge.pinyin4j)和icu4j依赖(com.ibm.icu.text),并且借鉴Java 8中字符串比对的源码给出了两套方案来判断、比较并排序处理含有中文/汉字字符的字符串,兼容性和通用性较好,并且给出了排序示例和详细的方法注解。
如需浏览,请详见目录,即可快速定位到需要的内容。之后将会持续修复Bug、并且更新、优化完善,如需及时收到最新内容,记得关注、收藏一下┗|`O′|┛ 嗷~~
一、方案一
比较处理过程直观,便于自定义,在一些细节处理上可以自行优化或加入符合实际需求的逻辑处理(支持数字排在最前,英文字母其次,汉字则按照拼音进行排序)
1、依赖引入
利用Maven引入pinyin4j依赖:
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>2.5.1</version>
</dependency>
2、完整代码(含注解)
该工具类移植性,代码复用性较好,且支持含单个或多个中文/汉字、英文、数字以及特殊字符杂糅的字符串大小比对(支持数字排在最前,英文字母其次,汉字则按照拼音进行排序);且将以下代码内容复制后直接粘贴到项目中即可使用!
import net.sourceforge.pinyin4j.PinyinHelper;
import java.util.Objects;
/**
* 比对工具(支持中文/汉字比对)
*
* @author JZY
*/
public class CompareUtil {
private static final String REGEX = "[\u4e00-\u9fa5]";
/**
* 判断单个字符是否为汉字
*
* @param c 字符
* @return 是否为汉字
*/
public static boolean isHanZi(char c) {
return String.valueOf(c).matches(REGEX);
}
/**
* 比对两个字符(可含汉字)大小
*
* @param c1 字符1
* @param c2 字符2
* @return 两个字符相差的大小(返回结果大于0表示第一个字符大,返回结果小于0表示第二个字符大,返回结果等于0表示两个字符一样大)
* (针对两个读音和音调相同的中文/汉字字符,再次进行常规的编码大小比对)
*/
public static int compareCharWithHanZi(char c1, char c2) {
boolean b1 = isHanZi(c1);
boolean b2 = isHanZi(c2);
if (b1 && b2) {
int result = Objects.requireNonNull(getFirstStringFromHanyuPinyinStringArray(c1)).compareTo(Objects.requireNonNull(getFirstStringFromHanyuPinyinStringArray(c2)));
return result != 0 ? result : Character.compare(c1, c2);
} else {
return Character.compare(c1, c2);
}
}
/**
* 判断两个字符串(可含汉字)大小
*
* @param string1 字符串1
* @param string2 字符串2
* @return 两个字符串大小的比对结果(返回1表示第一个字符串大,返回-1表示第二个字符串大,返回0表示两个字符串一样大)
*/
public static int compareStringWithHanZi(String string1, String string2) {
char[] charArray1 = string1.toCharArray();
char[] charArray2 = string2.toCharArray();
int length1 = charArray1.length;
int length2 = charArray2.length;
int limit = Math.min(length1, length2);
int result;
for (int i = 0; i < limit; i++) {
result = compareCharWithHanZi(charArray1[i], charArray2[i]);
if (result != 0) {
return result > 0 ? 1 : -1;
}
}
if (length1 == length2) {
return 0;
}
return length1 > length2 ? 1 : -1;
}
/**
* 获取单个字符的第一个汉语拼音
*
* @param c 字符
* @return 汉字字符的第一个汉语拼音
*/
public static String getFirstStringFromHanyuPinyinStringArray(char c) {
String[] strings = PinyinHelper.toHanyuPinyinStringArray(c);
if (strings != null) {
return strings[0];
} else {
return null;
}
}
}
3、验证测试
定义一些字符和字符串
char c1 = 'c';
char c2 = 'k';
char c3 = '码';
char c4 = '大';
char c5 = '衣';
char c6 = '依';
char c7 = '#';
char c8 = 'π';
String string1 = "李四";
String string2 = "张三";
String string3 = "123王s五";
String string4 = "12a3王s五";
String string5 = "123王z五";
String string6 = "123王z五";
String string7 = "一一壹";
String string8 = "一一医";
测试 isHanZi()
@Test
void isHanZi() {
System.out.println(CompareUtil.isHanZi(c1));
System.out.println(CompareUtil.isHanZi(c2));
System.out.println(CompareUtil.isHanZi(c3));
System.out.println(CompareUtil.isHanZi(c4));
System.out.println(CompareUtil.isHanZi(c5));
System.out.println(CompareUtil.isHanZi(c6));
System.out.println(CompareUtil.isHanZi(c7));
System.out.println(CompareUtil.isHanZi(c8));
}
测试结果符合预期
测试 compareCharWithHanZi()
@Test
void compareCharWithHanZi()
System.out.println(CompareUtil.compareCharWithHanZi(c1, c2));
System.out.println(CompareUtil.compareCharWithHanZi(c1, c3));
System.out.println(CompareUtil.compareCharWithHanZi(c3, c4));
System.out.println(CompareUtil.compareCharWithHanZi(c5, c6));
System.out.println(CompareUtil.compareCharWithHanZi(c7, c8));
}
测试结果符合预期
测试 compareStringWithHanZi()
@Test
void compareStringWithHanZi() {
System.out.println(CompareUtil.compareStringWithHanZi(string1, string2));
System.out.println(CompareUtil.compareStringWithHanZi(string1, string3));
System.out.println(CompareUtil.compareStringWithHanZi(string3, string5));
System.out.println(CompareUtil.compareStringWithHanZi(string4, string5));
System.out.println(CompareUtil.compareStringWithHanZi(string5, string6));
System.out.println(CompareUtil.compareStringWithHanZi(string7, string8));
}
测试结果符合预期
测试 getFirstStringFromHanyuPinyinStringArray()
@Test
void getFirstStringFromHanyuPinyinStringArray() {
System.out.println(CompareUtil.getFirstStringFromHanyuPinyinStringArray(c1));
System.out.println(CompareUtil.getFirstStringFromHanyuPinyinStringArray(c2));
System.out.println(CompareUtil.getFirstStringFromHanyuPinyinStringArray(c3));
System.out.println(CompareUtil.getFirstStringFromHanyuPinyinStringArray(c4));
System.out.println(CompareUtil.getFirstStringFromHanyuPinyinStringArray(c5));
System.out.println(CompareUtil.getFirstStringFromHanyuPinyinStringArray(c6));
System.out.println(CompareUtil.getFirstStringFromHanyuPinyinStringArray(c7));
System.out.println(CompareUtil.getFirstStringFromHanyuPinyinStringArray(c8));
}
测试结果符合预期
4、排序示例
String数组排序
@Test
void compareList() {
LinkedList<String> linkedList = new LinkedList<>();
linkedList.add("张三");
linkedList.add("李四");
linkedList.add("王五");
linkedList.add("赵六");
linkedList.add("王二麻子");
System.out.println("排序前:" + linkedList);
linkedList.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return CompareUtil.compareStringWithHanZi(o1, o2);
}
});
System.out.println("排序(升序)后:" + linkedList);
}
运行结果符合预期
如果要降序排序,将自定义比对器的返回结果取反即可:
return CompareUtil.compareStringWithHanZi(o1, o2);
修改为
return -CompareUtil.compareStringWithHanZi(o1, o2);
Tip:如果采用的是Java 8,即可使用Lambda表达式简化代码:
linkedList.sort((o1, o2) -> CompareUtil.compareStringWithHanZi(o1, o2));
此处的Lambda表达式甚至可以替换为方法参考,进一步简化代码:
linkedList.sort(CompareUtil::compareStringWithHanZi);
当然实际使用中,需要根据具体的业务需求来自定义Comparator,以上仅供参考!
复杂对象排序
先定义一个复杂对象
class User {
private HashMap<String, Object> information;
public User() {
}
public User(HashMap<String, Object> information) {
this.information = information;
}
public HashMap<String, Object> getInformation() {
return information;
}
public void setInformation(HashMap<String, Object> information) {
this.information = information;
}
@Override
public String toString() {
return "User{" +
"information=" + information +
'}';
}
}
@Test
void compareObject() {
User user1 = new User();
HashMap<String, Object> hashMap1 = new HashMap<>();
hashMap1.put("name", "张三");
hashMap1.put("age", 25);
user1.setInformation(hashMap1);
User user2 = new User();
HashMap<String, Object> hashMap2 = new HashMap<>();
hashMap2.put("name", "李四");
hashMap2.put("age", 20);
user2.setInformation(hashMap2);
User user3 = new User();
HashMap<String, Object> hashMap3 = new HashMap<>();
hashMap3.put("name", "王五");
hashMap3.put("age", 30);
user3.setInformation(hashMap3);
LinkedList<User> linkedList = new LinkedList<>();
linkedList.add(user1);
linkedList.add(user2);
linkedList.add(user3);
System.out.println("排序前:" + linkedList);
linkedList.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return CompareUtil.compareStringWithHanZi(String.valueOf(o1.getInformation().get("name")), String.valueOf(o2.getInformation().get("name")));
}
});
System.out.println("排序(升序)后:" + linkedList);
}
运行结果符合预期
如果要降序排序,将自定义比对器的返回结果取反即可:
return CompareUtil.compareStringWithHanZi(String.valueOf(o1.getInformation().get("name")), String.valueOf(o2.getInformation().get("name")));
修改为
return -CompareUtil.compareStringWithHanZi(String.valueOf(o1.getInformation().get("name")), String.valueOf(o2.getInformation().get("name")));
Tip:如果采用的是Java 8,即可使用Lambda表达式简化代码:
linkedList.sort((o1, o2) -> CompareUtil.compareStringWithHanZi(String.valueOf(o1.getInformation().get("name")), String.valueOf(o2.getInformation().get("name"))));
当然实际使用中,需要根据具体的业务需求来自定义Comparator,以上仅供参考!
二、方案二
使用com.ibm.icu.text.Collator进行排序(支持数字排在最前,英文字母其次,汉字则按照拼音进行排序)
效率较高,兼容性较好,但是封装程度较高,如果有特殊业务需求的比较规则,可能需要在比较逻辑层面上自行重写方法
1、依赖引入
利用Maven引入icu4j依赖:
<dependency>
<groupId>com.ibm.icu</groupId>
<artifactId>icu4j</artifactId>
<version>68.2</version>
</dependency>
2、使用演示
@Test
void comIbmIcuTextCollator() {
List<String> stringList = new ArrayList<>();
stringList.add("中国");
stringList.add("中心");
stringList.add("网易汽车");
stringList.add("新民汽车网");
stringList.add("钛媒体");
stringList.add("瘾科技");
stringList.add("昕薇网");
stringList.add("安倍");
System.out.println("使用com.ibm.icu.text.Collator进行排序");
System.out.println("排序前:" + stringList);
com.ibm.icu.text.Collator comIbmIcuTextCollator = com.ibm.icu.text.Collator.getInstance();
stringList.sort(new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return comIbmIcuTextCollator.compare(o1, o2);
}
});
System.out.println("排序后:" + stringList);
}
运行结果
如果要降序排序,调换入参顺序即可:
return comIbmIcuTextCollator.compare(o1, o2);
修改为
return comIbmIcuTextCollator.compare(o2, o1);
Tip:如果采用的是Java 8,即可使用Lambda表达式简化代码:
stringList.sort(comIbmIcuTextCollator::compare);
当然实际使用中,需要根据具体的业务需求来自定义Comparator,以上仅供参考!
其中这里要特别指出的是,网上也有使用Java JDK中自带的Collator:java.text.Collator;但是并不建议使用!为了对比两者的区别,采用java.text.Collator对上面的数据再进行一次排序
@Test
void javaTextCollator() {
List<String> stringList = new ArrayList<>();
stringList.add("中国");
stringList.add("中心");
stringList.add("网易汽车");
stringList.add("新民汽车网");
stringList.add("钛媒体");
stringList.add("瘾科技");
stringList.add("昕薇网");
stringList.add("安倍");
System.out.println("使用java.text.Collator进行排序");
System.out.println("排序前:" + stringList);
java.text.Collator javaTextCollator = java.text.Collator.getInstance();
stringList.sort(javaTextCollator::compare);
System.out.println("排序后:" + stringList);
}
运行结果
发现“中国”、“中心”等字词排序位置不符合要求(理应是根据:数字排在最前,英文字母其次,汉字则按照拼音进行排序)
主要是因为java.text.Collator使用的顺序是其字符串包含的字符在指定语言当中的顺序(譬如中文汉字编著入库的顺序),是locale敏感的,为此不能做到任何环境下所有字符统一处理(可能同样的数据在不同的条件下处理结果不一致)
- 为此建议使用com.ibm.icu.text.Collator;官网的介绍:ICU是成熟的,广泛使用的C/C++和Java库集,为软件应用程序提供Unicode和全球化支持。ICU具有广泛的可移植性,可以在所有平台上以及C/C++和Java软件之间为应用程序提供相同的结果。它还定义了数据和算法,以实现高效,一致的文本处理。这简化了高级处理,并确保所有兼容软件产生相同的结果。基于Unicode的全球化软件可最大程度地扩大市场范围并最小化成本。全球化软件可以一次构建和安装,但是可以处理来自世界各地的用户和来自世界各地的用户的文本,并适应他们的文化习俗。通过消除每种语言的构建,安装和维护更新,将成本降到最低
- 为了保证兼容性和一致性,采用com.ibm.icu.text.Collator更具有优势!
3、排序示例
复杂对象排序
先定义一个复杂对象
/**
* 用户实体类
*/
class User implements Serializable {
private static final long serialVersionUID = 1L;
private HashMap<String, Object> information;
public User() {
}
public User(HashMap<String, Object> information) {
this.information = information;
}
public HashMap<String, Object> getInformation() {
return information;
}
public void setInformation(HashMap<String, Object> information) {
this.information = information;
}
@Override
public String toString() {
return "User{" +
"information=" + information +
'}';
}
}
@Test
void compareObjectUseComIbmIcuTextCollator() {
User user1 = new User();
HashMap<String, Object> hashMap1 = new HashMap<>();
hashMap1.put("name", "张三");
hashMap1.put("age", 25);
user1.setInformation(hashMap1);
User user2 = new User();
HashMap<String, Object> hashMap2 = new HashMap<>();
hashMap2.put("name", "李四");
hashMap2.put("age", 20);
user2.setInformation(hashMap2);
User user3 = new User();
HashMap<String, Object> hashMap3 = new HashMap<>();
hashMap3.put("name", "王五");
hashMap3.put("age", 30);
user3.setInformation(hashMap3);
LinkedList<User> linkedList = new LinkedList<>();
linkedList.add(user1);
linkedList.add(user2);
linkedList.add(user3);
System.out.println("排序前:" + linkedList);
com.ibm.icu.text.Collator comIbmIcuTextCollator = com.ibm.icu.text.Collator.getInstance();
linkedList.sort(new Comparator<User>() {
@Override
public int compare(User o1, User o2) {
return comIbmIcuTextCollator.compare(o1.getInformation().get("name"), o2.getInformation().get("name"));
}
});
System.out.println("排序(升序)后:" + linkedList);
}
运行结果符合预期
如果要降序排序,将比对器的返回结果取反即可:
return comIbmIcuTextCollator.compare(o1.getInformation().get("name"), o2.getInformation().get("name"));
修改为
return -comIbmIcuTextCollator.compare(o1.getInformation().get("name"), o2.getInformation().get("name"));
或者调换入参顺序
return comIbmIcuTextCollator.compare(o2.getInformation().get("name"), o1.getInformation().get("name"));
Tip:如果采用的是Java 8,即可使用Lambda表达式简化代码:
linkedList.sort((o1, o2) -> comIbmIcuTextCollator.compare(o1.getInformation().get("name"), o2.getInformation().get("name")));
当然实际使用中,需要根据具体的业务需求来自定义Comparator,以上仅供参考!
免责申明:相关文章及资料仅供学习交流使用,禁止一切不正当行为,如由此产生相关责任,自行承担
Tip:如需转发或引用相关内容,请务必附带原链接
如果对你有帮助的话,麻烦关注一波,并且点赞、收藏、转发一下哦o( ̄︶ ̄)o!如果有问题或者发现Bug欢迎提出反馈!