Java
sort
文字列
数字

[Java] 文字列と数字が混在している場合のソートTips

前回の[Java8] 従業員のソートから考えるComparableとComparatorの適切な使い方に引き続きソート時のTipsです。

探してみてもあまり見つからなかったのですが、

["2", "32", "100", "13", "1", "21"]

こんな配列や

["A02", "12", "A11", "A01", "B01", "A21"]

こんな配列を「よしなに」ソートをしたい!と思ったことありませんか?

いちいち数字にパースするもの面倒だし、パースする場合は数字じゃない場合の制御も面倒ですよね。
大した内容ではないのですが、わりと簡単な処理で解決できるなぁと思ったので、処理内容を書いておきます。

期待されるソート結果
["1", "2", "13", "21", "32", "100"]
// -> ソート前 ["2", "32", "100", "13", "1", "21"]

["12", "A01", "A02", "A11", "A21", "B01"]
// -> ソート前 ["A02", "12", "A11", "A01", "B01", "A21"]

結論を書くと、まず文字列の長さを比較した上で、文字列同士の比較することです。

実際に書いてみます。

文字列の長さを比較してから、文字列同士を比較
pubic List<String> sort(List<String> list) {
    list.sort((s1, s2) -> {
        if (compareLength(s1, s2) == 0) {
            return s1.compareTo(s2);
        }
        return compareLength(s1, s2);
    });
    return list;
}

private int compareLength(String s1, String s2) {
    if (s1.length() > s2.length()) {
        return 1;
    } else if (s1.length() < s2.length()) {
        return -1;
    } else {
        return 0;
    }
}
実行結果
List<String> list1 = new ArrayList<>(Arrays.asList("2", "32", "100", "13", "1", "21"));
List<String> list2 = new ArrayList<>(Arrays.asList("A02", "12", "A11", "A01", "B01", "A21"));
List<String> list3 = new ArrayList<>(Arrays.asList("0002", "0032", "0100", "0013", "0001", "0021"));

System.out.println(ArrayUtils.toString(sort(list1)));
System.out.println(ArrayUtils.toString(sort(list2)));

// 実行結果
[1, 2, 13, 21, 32, 100]
[12, A01, A02, A11, A21, B01]

ちなみに桁が0埋めされているケースでも、桁数が同じであれば想定通りにソートしてくれます。

0埋めの場合
List<String> list = new ArrayList<>(Arrays.asList("0002", "0032", "0100", "0013", "0001", "0021"));

System.out.println(ArrayUtils.toString(sort(list)));

// 実行結果
[0001, 0002, 0013, 0021, 0032, 0100]

「よしなに」ということでどのようなシチュエーションでも使えるものではないと思いますが、個人的には思い通りの結果が得られて満足しております。
もっとスマートに処理できるようなロジックだったり、この方法だと「よしなに」処理できないようなリストがあれば是非共有していただけると嬉しいです!

追記

下記のようにするとよりシンプルになるとコメントいただきました!

List<String> list = Arrays.asList("2", "32", "100", "13", "1", "21");
list.sort(Comparator.comparing(String::length).thenComparing(Function.identity());
System.out.println(list);