java-beginner.com ブログ

プログラミングを学習するブログ(Javaをメインに)

ソートで大文字小文字の順番を逆にする

投稿日:

最終更新日:2016年11月27日

アイキャッチ

こんにちは。「Javaを復習する初心者」です。

今回はListの英単語をソートする処理について、大文字小文字の順番を逆にすることをやってみました。

Collectionsクラスにsortというメソッドがあります。引数が一つのメソッドsort(List<T> list) 、二つのメソッドsort(List<T> list, Comparator<? super T> c) があります。第一引数に指定したListがソートれます。引数が二つのメソッドではComparatorインターフェースの実装クラスを指定すること、ソート順を自作することができます。

この記事の前半では、第二引数にStringクラスに用意されているCASE_INSENSITIVE_ORDERを使って、出力を確認しました。このクラスを使うと大文字小文字を無視するという順序でソートします。

この記事の後半では、Comparatorインターフェースの実装クラスを定義して、大文字小文字の順番を逆にしてソートしました。

StringクラスのCASE_INSENSITIVE_ORDER

以下のようにCASE_INSENSITIVE_ORDERでソートしてみました。

ソース(大文字小文字を無視)

        String[] strs = {"aA", "aa", "bb", "bB", "ac", "ab", "BA", "AC", "AB", "ca", "cA", "cB", "cb"};

        // 大文字小文字を無視するソート
        List<String> list = Arrays.asList(strs.clone());
        Collections.sort(list, String.CASE_INSENSITIVE_ORDER);

        // 出力
        System.out.println("ソート前");
        System.out.println(Arrays.toString(strs));
        System.out.println("ソート後");
        System.out.println(list);

結果

ソート前
[aA, aa, bb, bB, ac, ab, BA, AC, AB, ca, cA, cB, cb]
ソート後
[aA, aa, ab, AB, ac, AC, BA, bb, bB, ca, cA, cB, cb]

Collections#sortの第二引数にString.CASE_INSENSITIVE_ORDERを指定してます。なので、大文字小文字を無視してソートされます。

文字コードについてですが、以下の順になっています。

A~Z 0x41~0x5a
a~z 0x61~0x7a

大文字の方が先に定義されてます。辞書式順序というと大文字のあとが小文字のようです。ちなみに文字コードは例えば「Integer.toHexString((int)’A’)」という記述で出力できます。

出力結果ですが、「ab, AB, ac」という並びに注目してください。これは大文字小文字を無視しているので、「AB」は「ab」と同じ扱いになります。なので、「ac」より前になります。

最後の「ca, cA, cB, cb」という並びは大文字小文字が無視されて、もともとの順序がそのままという結果です。

大文字小文字を逆にする実装

Collections#sort()メソッドの第二引数を自作クラスにすることで、大文字小文字の順序を逆にするソート順を定義します。

ソース

        String[] strs = {"aA", "aa", "bb", "bB", "ac", "ab", "BA", "AC", "AB", "ca", "cA", "cB", "cb"};

        // 大文字小文字を逆にするソート
        List<String> list = Arrays.asList(strs.clone());
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return change(s1).compareTo(change(s2));
            }

            static final int offset = 32;

            private String change(String s) {
                StringBuilder sb = new StringBuilder();
                for (int i = 0; i < s.length(); i++) {
                    char c = s.charAt(i);
                    if ('A' <= c && c <= 'Z') {
                        c += offset;
                    } else if ('a' <= c && c <= 'z') {
                        c -= offset;
                    }
                    sb.append(c);
                }
                return sb.toString();
            }
        });

        // 出力
        System.out.println("ソート前");
        System.out.println(Arrays.toString(strs));
        System.out.println("ソート後");
        System.out.println(list);

結果

ソート前
[aA, aa, bb, bB, ac, ab, BA, AC, AB, ca, cA, cB, cb]
ソート後
[aa, ab, ac, aA, bb, bB, ca, cb, cA, cB, AB, AC, BA]

匿名クラスの箇所箇所で、compare()メソッドは必ず実装しなくてはなりません。compare()の戻り値を工夫することでソート順の大文字小文字を逆転させます。そのために、change()メソッドを定義しました。要するにs1とs2を比較するのではなく、それぞれのchangeメソッドの結果を比較するのですが、発想は単純で、change()メソッドで大文字と小文字を入れ替えた文字列を返却するするという方法です。

change()メソッドは引数のString型に対して、先頭から一文字ずつ変換してStringBuilderに格納しています。if文の箇所で大文字と小文字を変換しています。大文字と対応する小文字の文字コードの差が32で、この分だけ加減しています。

出力結果では先頭が小文字のものが先に並ぶ結果となっています。