在 Java 開發中,常會遇到類似 a20 和 a9 這樣的字母數字混合字符串。若直接使用 String.compareTo(),結果會是 a20 < a9(按字典序),但業務上往往期望 a20 > a9(按數值大小)。這就是典型的「自然排序」需求,也是常見的面試題目。
List<Object>,其中數字部分存為 Integer,字母部分存為 String(或 StringBuffer)。compareTo,以處理 a02 vs a2 這類前導零情況。採用單次遍歷,利用 StringBuffer 暫存字母,Integer 累加數字。遇到類型切換時,將暫存內容加入列表並重置暫存器。
@Override
public int compareTo(MyString another) {
int n = Math.min(list.size(), another.list.size());
for (int i = 0; i < n; i++) {
Object o1 = list.get(i), o2 = another.list.get(i);
int cmp;
if (o1 instanceof Integer && o2 instanceof Integer) {
cmp = (Integer)o1 - (Integer)o2;
} else {
cmp = o1.toString().compareTo(o2.toString());
}
if (cmp != 0) return cmp;
}
// 前綴相同,按原始字符串比較(保留前導零差異)
return compareToLikeString(another.string);
}
若直接用 string.compareTo(another.string),a02 會小於 a2(因 '0' < '2')。自定義 compareToLikeString 可按字符逐個比較,若完全相同再比長度,確保 a02 > a2 符合直覺。
public class MyString implements Comparable<MyString> {
private final String string;
private final List<Object> list = new ArrayList<>();
public MyString(String string) {
this.string = string;
parse();
}
private void parse() {
StringBuffer sb = new StringBuffer();
int num = 0;
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
if (c >= '0' && c <= '9') {
if (sb.length() > 0) { list.add(sb.toString()); sb.setLength(0); }
num = num * 10 + (c - '0');
} else {
if (num != 0) { list.add(num); num = 0; }
sb.append(c);
}
}
if (sb.length() > 0) list.add(sb.toString());
else if (num != 0) list.add(num);
}
// ... compareTo 實現同上 ...
}
MyString m1 = new MyString("b9c");
MyString m2 = new MyString("b09c");
System.out.println(m1.compareTo(m2)); // 輸出 > 0,表示 b9c > b09c
通過實現 Comparable 接口並結合「字母數字分離解析 + 逐元素比較」策略,即可優雅解決混合字符串的自然排序問題。此方案可擴展至文件名排序、版本號比較等場景。