北海道苫小牧市出身の初老PGが書くブログ

永遠のプログラマを夢見る、苫小牧市出身のおじさんのちらしの裏

StringUtils.padding(int repeat, char padChar)

皆さん、ソースコードは下手な小説より楽しいですよヽ( ´-`)ノ。今日はJakarta commonsより、padding()です。文字padCharをrepeat回だけ繰り返した文字列を返すだけの関数です。

    private static String padding(int repeat, char padChar) {
        // be careful of synchronization in this method
        // we are assuming that get and set from an array index is atomic
        String pad = PADDING[padChar];
        if (pad == null) {
            pad = String.valueOf(padChar);
        }
        while (pad.length() < repeat) {
            pad = pad.concat(pad);
        }
        PADDING[padChar] = pad;
        return pad.substring(0, repeat);
    }

はて? と思えれば、きちんとソースを読めてる人(笑)。繰り返しの実装するだけなのに妙に複雑ですね。特に、String PADDING[]。こいつの宣言を探すと、序盤にこんな摩訶不思議なコードが出てきます。

    private static final String[] PADDING = new String[Character.MAX_VALUE];

    static {
        // space padding is most common, start with 64 chars
        PADDING[32] = "                                                                ";
    }

PADDINGは文字列の配列で、文字の個数ぶんだけ領域があるみたいです。じゃあ、32番目の要素だけ初期化してるのはなぜ? しかも空白64byte。

これは、関数内のifとwhileを見ると答えが見えてきます。PADDING配列を調べて、中身がなければ作成、個数が足りなければ追加してます。このロジックは・・・そう、キャッシュですね。毎回padding文字列を繰り返すのは無駄なので、文字をつないだものをキャッシュに突っ込んでおいて、そのキャッシュからsubstring()で返してるわけです。

で、staticの中では、最もよく使われるスペース*1の64個分のpadding文字列を確保していたってことです。深い実装ですねー。

でも、ここで改善点とかも浮かんできます。特に、whileでの追加部分。そんなにパフォーマンスにこだわるなら、StringBuilderを使ってもいい気がします。後、1文字ずつキャッシュを拡張するんじゃなく、64byteずつ拡張するとか。

それと、この実装はキャッシュに文字列を突っ込むので、repeatに大きな数を与え過ぎると大変なことになります。生成される巨大な文字列を、利用者はパッと使ってすぐ捨てるつもりでも、キャッシュにはいつまでも残ってしまうと言うことになります。

まあ、用途としてはスペースを5〜10個つけるってくらいを想定しているんで、こんな実装なんでしょう。巨大な文字列や他の文字を追加するのに利用することは、あまり想定していないってことですね。

*1:0x20=32 ←2009-04-14 追記