如何按后缀字母优先、前缀数字排序字符串列表?自定义Comparator失效
问题
我有如下字符串列表:
(2D, 1D, 1Y, 23M, 4M, 2Y, 12D)
需要实现先按后缀字母排序、再按前缀数字排序的效果,期望结果为:
(1D, 2D, 12D, 4M, 23M, 1Y, 2Y)
我尝试了以下代码但未能生效:
Collections.sort(myList, new MyComparator()); public class MyComparator implements Comparator { @Override public int compare(Object o1, Object o2) { String s1 = (String)o1; String s2 = (String)o2; String strPart1 = s1.split("")[1]; int intPart1 = Integer.parseInt(s1.split("")[0]); String strPart2 = s2.split("")[1]; int intPart2 = Integer.parseInt(s2.split("")[0]); int strCompareResult = strPart1.compareTo(strPart2); if(0 == strCompareResult ) { return intPart1 - intPart2; } else { return strCompareResult; } } }
请问该如何解决?
问题原因
你的代码核心问题在于字符串拆分逻辑错误:
s1.split("")会将字符串拆分为单个字符数组,比如"12D"会被拆成["1","2","D"],此时取索引0只能拿到第一个数字字符,索引1拿到第二个数字字符,根本无法正确提取完整的数字部分和后缀字母。- 当数字部分是多位数时(如
12D、23M),这种拆分方式会导致解析数字时抛出NumberFormatException,因为你只取了单个字符转数字,而非完整的数字段。
正确实现
可以通过正则表达式或遍历字符串的方式,拆分出每个元素的数字部分和后缀字母,再执行排序逻辑。以下是两种可靠实现:
方案一:正则表达式(简洁高效)
import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class MyComparator implements Comparator<String> { // 匹配数字+后缀字母的正则,分组1为数字,分组2为后缀 private static final Pattern PATTERN = Pattern.compile("(\\d+)([DMY])"); @Override public int compare(String s1, String s2) { // 解析第一个字符串 Matcher matcher1 = PATTERN.matcher(s1); matcher1.find(); int num1 = Integer.parseInt(matcher1.group(1)); String suffix1 = matcher1.group(2); // 解析第二个字符串 Matcher matcher2 = PATTERN.matcher(s2); matcher2.find(); int num2 = Integer.parseInt(matcher2.group(1)); String suffix2 = matcher2.group(2); // 先比较后缀字母 int suffixCompare = suffix1.compareTo(suffix2); if (suffixCompare != 0) { return suffixCompare; } // 后缀相同则比较数字大小 return Integer.compare(num1, num2); } } // 使用方式不变 Collections.sort(myList, new MyComparator());
方案二:遍历字符串拆分(无正则依赖)
import java.util.Collections; import java.util.Comparator; import java.util.List; public class MyComparator implements Comparator<String> { @Override public int compare(String s1, String s2) { // 提取s1的数字和后缀 int splitIndex1 = findSplitIndex(s1); int num1 = Integer.parseInt(s1.substring(0, splitIndex1)); String suffix1 = s1.substring(splitIndex1); // 提取s2的数字和后缀 int splitIndex2 = findSplitIndex(s2); int num2 = Integer.parseInt(s2.substring(0, splitIndex2)); String suffix2 = s2.substring(splitIndex2); int suffixCompare = suffix1.compareTo(suffix2); if (suffixCompare != 0) { return suffixCompare; } return Integer.compare(num1, num2); } // 找到第一个非数字字符的索引,即后缀的起始位置 private int findSplitIndex(String s) { int index = 0; while (index < s.length() && Character.isDigit(s.charAt(index))) { index++; } return index; } }
补充说明
- 建议使用泛型
Comparator<String>代替原始Comparator,避免强制类型转换,提升代码安全性。 - 使用
Integer.compare(num1, num2)比直接num1 - num2更稳妥,可避免整数溢出问题。
内容的提问来源于stack exchange,提问作者user3809938
相关产品推荐
相关产品推荐

