龙空技术网

关于携带中文数字字符串的排序解决方案

但求无Bug 468

前言:

目前看官们对“java字符数字排序”大致比较注重,我们都需要剖析一些“java字符数字排序”的相关知识。那么小编在网络上收集了一些关于“java字符数字排序””的相关资讯,希望姐妹们能喜欢,看官们快快来了解一下吧!

我们在日常的开发中,偶尔会遇到这么一种场景,那就是对携带中文数字的字符串进行排序的问题,比如:“XXX号楼XX单元XXX号”等。说实话,作为一个懒人而言,寻找成熟的工具类是最快捷和省事的方法。但是我查了很久,都未发现可用的工具类。虽然在CSDN或博客园有人分享过类似的写法,但是测试之后总是觉得不尽如人意。思量再三后,还是决定自己实现。

其实仔细想想,排序这个事也没那么难。最重要的是想明白按照哪种规则进行排序即可。我想出的规则如下:

首先建立转换的字典表,也就是常见数字、单位与对应阿拉伯数字的映射关系。比较的时候通过两个索引来从前往后依次比较对应索引处的字符,若相等,则继续进行下一位的比较。当对应索引处的字符不相等时,则判断是否为数字,如果是则各自向前推进,直到遇到非数字为止,接着将获取到的字符串数字转换为Long类型后进行比较。若判断是字母,则直接进行比较。若判断为字典表中的值,则各自向前推进,直到遇到非字典中的值后,将获取到的字符串转换为Long类型后进行比较。若3,4,5三种情况都不满足,则按照默认的中文排序。

下面开始对应的代码实现。

建立转换的字典表

下面是建立字典表的代码:

		private static Set<Character> keyList = new HashSet<>();    private static Map<Character, Long> basicNumMap = new HashMap<>();    private static Map<Character, Long> unitMap = new HashMap<>();    private static Map<Character, Long> bigUnitMap = new HashMap<>();    static {        // 初始化basicNumMap        basicNumMap.put('零', 0L);        basicNumMap.put('一', 1L);        basicNumMap.put('二', 2L);        basicNumMap.put('三', 3L);        basicNumMap.put('四', 4L);        basicNumMap.put('五', 5L);        basicNumMap.put('六', 6L);        basicNumMap.put('七', 7L);        basicNumMap.put('八', 8L);        basicNumMap.put('九', 9L);        // 初始化unitMap        unitMap.put('十', 10L);        unitMap.put('百', 100L);        unitMap.put('千', 1000L);        // 初始化unitMap        bigUnitMap.put('万', 10000L);        bigUnitMap.put('亿', 1_0000_0000L);        // 初始化keyList        keyList.addAll(basicNumMap.keySet());        keyList.addAll(unitMap.keySet());        keyList.addAll(bigUnitMap.keySet());    }
中文转数字

上面说到,若对应索引处的字符为字典中的值,意味着可以转换为数字。因此需提供一个将中文数字转换为对应阿拉伯数字的方法,下面是对应的代码:

/**     * 提取字符串中的数字(若存在非数字的汉字则进行忽略)     *     * @param str     * @return     */    private static long chineseToNumber(String str) {        // 若字符串为空,则返回默认值-1        if (str == null || str.length() == 0) {            return -1L;        }        /*        进行运算的栈,运算规则是:        1、若获取到的字符为非字典值,则意味着该字符串不是数字,就抛出异常        2、若为从零到九的基础数字,则直接压入栈中;        3、若为十,百,千这种普通的计数单位,则需判断栈是否为空,不为空时将栈中的数字取出与该计数单位相乘后再次压入栈中。            为空则直接将对应的计数单位压入栈中。        4、若为万,亿这种大的计数单位,则将栈中的元素进行累加后获得栈累加值,接着判断当前的大计数位是否比之前的大,            若大,则需将栈累加值与之前的全局累加值想加后再进行相乘,将计算的值赋给全局累加值。若不大,则将栈累加值与大计数单位相乘            后再与全局累加值进行相加。        5、当退出循环后,需检查操作数栈是否为空,若不为空,则将栈中的值依次取出累加到全局累计值上。、        6、将最后的全局累加值进行返回就可得到转换后的数字         */        LinkedList<Long> operateNumStack = new LinkedList<>();        long maxBigUnit = 0L;        long num = 0L, currentSum;        for (int i = 0; i < str.length(); i++) {            char currCh = str.charAt(i);            if (!keyList.contains(currCh)) {                throw new RuntimeException("该字符串不能转换为数字");            }            if (basicNumMap.containsKey(currCh)) {                operateNumStack.push(basicNumMap.get(currCh));            } else if (unitMap.containsKey(currCh)) {                if (operateNumStack.isEmpty()) {                    operateNumStack.push(unitMap.get(currCh));                } else {                    operateNumStack.push(operateNumStack.pop() * unitMap.get(currCh));                }            }else if (bigUnitMap.containsKey(currCh)) {                currentSum = 0L;                while (!operateNumStack.isEmpty()) {                    currentSum += operateNumStack.pop();                }                Long currentBigUnit = bigUnitMap.get(currCh);                if (currentBigUnit <= maxBigUnit) {                    num += currentSum * bigUnitMap.get(currCh);                } else {                    num = (num + currentSum) * bigUnitMap.get(currCh);                    maxBigUnit = currentBigUnit;                }            }        }        while (!operateNumStack.isEmpty()) {            num += operateNumStack.pop();        }        return num;    }
中文字符串比较器

对于Java来说,排序时如果不是数字或字母,则需要通过自定义比较器来实现排序。下面是获取自定义排序器的代码:

/**     * 获取中文比较的比较器     *     * @return     */    public static Comparator<String> chineseComp() {        return new Comparator<String>() {            @Override            public int compare(String str1, String str2) {                int i1 = 0, i2 = 0;                StringBuilder sb1 = new StringBuilder();                StringBuilder sb2 = new StringBuilder();                Collator collator = Collator.getInstance(Locale.CHINESE);                while (i1 < str1.length() && i2 < str2.length()) {                    char ch1 = str1.charAt(i1);                    char ch2 = str2.charAt(i2);                    if (ch1 == ch2) {                        i1++;                        i2++;                        continue;                    }                    if (Character.isDigit(ch1) && Character.isDigit(ch2)) {                        // 收集字符串1中的数字                        while (Character.isDigit(str1.charAt(i1)) && i1 < str1.length()) {                            sb1.append(str1.charAt(i1++));                        }                        // 收集字符串2中的数字                        while (Character.isDigit(str2.charAt(i2)) && i2 < str2.length()) {                            sb2.append(str2.charAt(i2++));                        }                        long l1 = Long.parseLong(sb1.toString());                        long l2 = Long.parseLong(sb2.toString());                        if (l1 - l2 != 0) {                            return Long.compare(l1, l2);                        }                        sb1.setLength(0);                        sb2.setLength(0);                    } else if (String.valueOf(ch1).matches("[a-zA-Z]")                            && String.valueOf(ch2).matches("[a-zA-Z]")) {                        return Character.compare(Character.toLowerCase(ch1), Character.toLowerCase(ch2));                    } else if (keyList.contains(ch1) && keyList.contains(ch2)) {                        // 收集字符串1中的数字                        while (keyList.contains(str1.charAt(i1)) && i1 < str1.length()) {                            sb1.append(str1.charAt(i1++));                        }                        // 收集字符串2中的数字                        while (keyList.contains(str2.charAt(i2)) && i2 < str2.length()) {                            sb2.append(str2.charAt(i2++));                        }                        long l1 = chineseToNumber(sb1.toString());                        long l2 = chineseToNumber(sb2.toString());                        if (l1 - l2 != 0) {                            return Long.compare(l1, l2);                        }                        sb1.setLength(0);                        sb2.setLength(0);                    } else {                        return collator.compare(Character.toString(ch1), Character.toString(ch2));                    }                }                return 1;            }        };    }

到此关键的代码就已经介绍完毕,下面给出所有的代码:

import java.text.Collator;import java.util.Arrays;import java.util.Comparator;import java.util.HashMap;import java.util.HashSet;import java.util.LinkedList;import java.util.Locale;import java.util.Map;import java.util.Set;public class ChineseCompUtils {    public static void main(String[] args) {        String[] strArr = {                "龙跃苑一区三号楼二单元101号", "龙跃苑二区四号楼五单元201号", "龙跃苑一区八号楼六单元101号",                "龙跃苑四区十二号楼五单元301号", "龙跃苑二区十七号楼二单元501号", "龙跃苑三区八号楼三单元401号",                "龙跃苑一区三号楼二单元201号", "龙跃苑一区九号楼二单元601号", "龙跃苑一区二十二号楼二单元402号",                "龙跃苑三区一号楼五单元301号", "龙跃苑三区四号楼一单元501号", "龙跃苑三区十一号楼六单元301号",                "龙跃苑二区三号楼二单元201号", "龙跃苑四区八号楼一单元301号", "龙跃苑二区二号楼四单元301号",                "龙跃苑四区三号楼九单元401号", "龙跃苑二区五号楼二单元201号", "龙跃苑四区二十八号楼二单元501号",                "龙跃苑二区三号楼一单元301号", "龙跃苑一区六号楼二单元401号", "龙跃苑一区十七号楼二单元301号",                "龙跃苑一区三号楼十七单元301号", "龙跃苑三区十一号楼二单元602号", "龙跃苑三区三号楼二单元201号",        };        Arrays.sort(strArr, chineseComp());        for (int i = 0; i < strArr.length; i++) {            System.out.println(strArr[i]);        }    }    private static Set<Character> keyList = new HashSet<>();    private static Map<Character, Long> basicNumMap = new HashMap<>();    private static Map<Character, Long> unitMap = new HashMap<>();    private static Map<Character, Long> bigUnitMap = new HashMap<>();    static {        // 初始化basicNumMap        basicNumMap.put('零', 0L);        basicNumMap.put('一', 1L);        basicNumMap.put('二', 2L);        basicNumMap.put('三', 3L);        basicNumMap.put('四', 4L);        basicNumMap.put('五', 5L);        basicNumMap.put('六', 6L);        basicNumMap.put('七', 7L);        basicNumMap.put('八', 8L);        basicNumMap.put('九', 9L);        // 初始化unitMap        unitMap.put('十', 10L);        unitMap.put('百', 100L);        unitMap.put('千', 1000L);        // 初始化unitMap        bigUnitMap.put('万', 10000L);        bigUnitMap.put('亿', 1_0000_0000L);        // 初始化keyList        keyList.addAll(basicNumMap.keySet());        keyList.addAll(unitMap.keySet());        keyList.addAll(bigUnitMap.keySet());    }    /**     * 获取中文比较的比较器     *     * @return     */    public static Comparator<String> chineseComp() {        return new Comparator<String>() {            @Override            public int compare(String str1, String str2) {                int i1 = 0, i2 = 0;                StringBuilder sb1 = new StringBuilder();                StringBuilder sb2 = new StringBuilder();                Collator collator = Collator.getInstance(Locale.CHINESE);                while (i1 < str1.length() && i2 < str2.length()) {                    char ch1 = str1.charAt(i1);                    char ch2 = str2.charAt(i2);                    if (ch1 == ch2) {                        i1++;                        i2++;                        continue;                    }                    if (Character.isDigit(ch1) && Character.isDigit(ch2)) {                        // 收集字符串1中的数字                        while (Character.isDigit(str1.charAt(i1)) && i1 < str1.length()) {                            sb1.append(str1.charAt(i1++));                        }                        // 收集字符串2中的数字                        while (Character.isDigit(str2.charAt(i2)) && i2 < str2.length()) {                            sb2.append(str2.charAt(i2++));                        }                        long l1 = Long.parseLong(sb1.toString());                        long l2 = Long.parseLong(sb2.toString());                        if (l1 - l2 != 0) {                            return Long.compare(l1, l2);                        }                        sb1.setLength(0);                        sb2.setLength(0);                    } else if (String.valueOf(ch1).matches("[a-zA-Z]")                            && String.valueOf(ch2).matches("[a-zA-Z]")) {                        return Character.compare(Character.toLowerCase(ch1), Character.toLowerCase(ch2));                    } else if (keyList.contains(ch1) && keyList.contains(ch2)) {                        // 收集字符串1中的数字                        while (keyList.contains(str1.charAt(i1)) && i1 < str1.length()) {                            sb1.append(str1.charAt(i1++));                        }                        // 收集字符串2中的数字                        while (keyList.contains(str2.charAt(i2)) && i2 < str2.length()) {                            sb2.append(str2.charAt(i2++));                        }                        long l1 = chineseToNumber(sb1.toString());                        long l2 = chineseToNumber(sb2.toString());                        if (l1 - l2 != 0) {                            return Long.compare(l1, l2);                        }                        sb1.setLength(0);                        sb2.setLength(0);                    } else {                        return collator.compare(Character.toString(ch1), Character.toString(ch2));                    }                }                return 1;            }        };    }    /**     * 提取字符串中的数字(若存在非数字的汉字则进行忽略)     *     * @param str     * @return     */    private static long chineseToNumber(String str) {        // 若字符串为空,则返回默认值-1        if (str == null || str.length() == 0) {            return -1L;        }        /*        进行运算的栈,运算规则是:        1、若获取到的字符未非字典值,意味着该字符串不是数字,就抛出异常        2、若为从零到九的基础数字,则直接压入栈中;        3、若为十,百,千这种普通的计数单位,则需判断栈是否为空,不为空时将栈中的数字取出与该计数单位相乘后再次压入栈中。            为空则直接将对应的计数单位压入栈中。        4、若为万,亿这种大的计数单位,则将栈中的元素进行累加后获得栈累加值,接着判断当前的大计数位是否比之前的大,            若大,则需将栈累加值与之前的全局累加值想加后再进行相乘,将计算的值赋给全局累加值。若不大,则将栈累加值与大计数单位相乘            后再与全局累加值进行相加。        5、当退出循环后,需检查操作数栈是否为空,若不为空,则将栈中的值依次取出累加到全局累计值上。、        6、将最后的全局累加值进行返回就可得到转换后的数字         */        LinkedList<Long> operateNumStack = new LinkedList<>();      // 操作数栈        long maxBigUnit = 0L;        long num = 0L, currentSum;        for (int i = 0; i < str.length(); i++) {            char currCh = str.charAt(i);            if (!keyList.contains(currCh)) {                throw new RuntimeException("该字符串不能转换为数字");            }            if (basicNumMap.containsKey(currCh)) {                operateNumStack.push(basicNumMap.get(currCh));            } else if (unitMap.containsKey(currCh)) {                if (operateNumStack.isEmpty()) {                    operateNumStack.push(unitMap.get(currCh));                } else {                    operateNumStack.push(operateNumStack.pop() * unitMap.get(currCh));                }            } else if (bigUnitMap.containsKey(currCh)) {                currentSum = 0L;                while (!operateNumStack.isEmpty()) {                    currentSum += operateNumStack.pop();                }                Long currentBigUnit = bigUnitMap.get(currCh);                if (currentBigUnit <= maxBigUnit) {                    num += currentSum * bigUnitMap.get(currCh);                } else {                    num = (num + currentSum) * bigUnitMap.get(currCh);                    maxBigUnit = currentBigUnit;                }            }        }        while (!operateNumStack.isEmpty()) {            num += operateNumStack.pop();        }        return num;    }}

执行结果如下所示:

标签: #java字符数字排序 #java对中文排序