Java比较、排序处理含有中文/汉字的字符串


引言

基于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欢迎提出反馈!

  • 7
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

6铭记6

打赏一下吧!^-^

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值