关于携带中文数字字符串的排序解决方案
我们在日常的开发中,偶尔会遇到这么一种场景,那就是对携带中文数字的字符串进行排序的问题,比如:"XXX号楼XX单元XXX号"等。说实话,作为一个懒人而言,寻找成熟的工具类是最快捷和省事的方法。但是我查了很久,都未发现可用的工具类。虽然在CSDN或博客园有人分享过类似的写法,但是测试之后总是觉得不尽如人意。思量再三后,还是决定自己实现。
其实仔细想想,排序这个事也没那么难。最重要的是想明白按照哪种规则进行排序即可。我想出的规则如下:首先建立转换的字典表,也就是常见数字、单位与对应阿拉伯数字的映射关系。比较的时候通过两个索引来从前往后依次比较对应索引处的字符,若相等,则继续进行下一位的比较。当对应索引处的字符不相等时,则判断是否为数字,如果是则各自向前推进,直到遇到非数字为止,接着将获取到的字符串数字转换为Long类型后进行比较。若判断是字母,则直接进行比较。若判断为字典表中的值,则各自向前推进,直到遇到非字典中的值后,将获取到的字符串转换为Long类型后进行比较。若3,4,5三种情况都不满足,则按照默认的中文排序。
下面开始对应的代码实现。建立转换的字典表
下面是建立字典表的代码: private static Set keyList = new HashSet<>(); private static Map basicNumMap = new HashMap<>(); private static Map unitMap = new HashMap<>(); private static Map 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 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 chineseComp() { return new Comparator() { @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 keyList = new HashSet<>(); private static Map basicNumMap = new HashMap<>(); private static Map unitMap = new HashMap<>(); private static Map 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 chineseComp() { return new Comparator() { @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 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; } }
执行结果如下所示:
OPPOFindX5Pro,玩的就是曲线,颜值必须做到极致我个人对于OPPO还是非常欣赏的,在如今大家都在堆料的竞争中,它会给到我一种行业清流的感觉。Find系列机型相信大家也都接触过了,大多数产品的曲线设计是得到了消费者认可的,非常具有
中国移动大方了?10年不换号的老用户,现能享受4个特权中国移动一直以来在人民心目中的形象算不上非常好。不管是业务办理上,还是服务质量上都被很多人吐槽过。信号也不算特别好,很多时候信号突然会中断,可以说在使用上让人非常苦恼。不过中国移动
安徽大学研发出全球领先机器人智能抓取装置记者5月6日从安徽大学获悉,该校电气工程与自动化学院陈文杰教授团队基于机构具身性智能理论,提出了一套创新并联自适应抓手设计技术,成功研发出针对异状零件抓取作业的多个系列单驱自适应多
我对中概互联网投资的反思2021年底我所写的关于互联网公司投资价值的思考文章,目前回溯来看,当时极度低估了中概股在美国的退市风险,也进一步充满了对政策的敬畏,不是一切判断都可以来源于逻辑的,深刻的发现了自
被质疑割韭菜!知识付费的生意还能做多久?来源中国青年报自从2013年,公众号罗辑思维推出付费会员制以来,知识付费一词便跃入大众的视野中。其后近10年间,我国知识付费行业这片蓝海以令人目眩的速度扩张。数据显示,2021年,
Java16新特性instanceof增强instanceof这个关键词,主要用来判断某个对象是不是某个类的实例。比如,有时候我们要处理一个类似这样的数据集MapString,ObjectdatanewHashMap()d
数据治理大数据湖仓一体开源框架数据治理大数据湖仓一体开源框架分为4部分1数据源业务库数据用户日志系统日志爬虫数据2构建集群Hadoop,HDFS,Yarn3。1数据采集数据采集工具SqoopFlumeCanal
软件测试工程师常见面试题2一,oracle与mysql数据库区别1。mysql中小型开源,oracle大型收费2。Mysql有自动增长的数据类型,oracle没有3。mysql默认不支持事物oracle是完
重磅!Gucci宣布接受比特币狗狗币支付目前的最新消息奢侈品牌GUCCI也要开始接受加密货币的支付了!根据最新的情报显示,GUCCI此举率先会在美国开始试点,从本月底开始。第一批支持加密货比的GUCCI门店位于纽约的Wo
PHP中arraysplice()函数对数组元素的添加修改删除arraysplice()函数从数组中可实现对数据中元素的添加修改删除操作,方便对数组中的值进行处理。语法arraysplice(array,start,length,array)
姚记科技务实进取,泛娱乐王国横空出世今天我们来聊聊旧貌换新颜的泛娱乐龙头姚记科技。经常打牌的朋友对这个名字一定不陌生,这家公司之前就叫姚记扑克,从上世纪90年代起就以扑克牌生产发家致富,如今已经具备规模效应与成本优势