Javaで文字の出現回数を数えたい

RuuMusicをアンドロイダーに載せていただきまして、急激にインストール数が増えてうはうはです。アンドロイダーさまさまです。 でまあ、改良を続けているわけなんですが。

ファイルパスの深さを知るためにスラッシュの数を数える、という処理があります。 ググったらなんか出て来た適当な書き方をしているのですが、どうやらこれがとても重いらしく。 しかたがないので、思い付いた4通りの書き方を試してみました。

一つめ。何も考えずに数える方法。

int simple(String str, char target){
    int count = 0;

    for(char x: str.toCharArray()){
        if(x == target){
            count++;
        }
    }

    return count;
}

考えてません。とてもシンプルです。

二つめ。replaceで数えたい文字を抜いた文字列を作って、長さを比較することで数える方法。

int replace(String str, char target){
    return str.length() - str.replace(target + "", "").length();
}

短かくて良いやってことで適当に採用したのですが、あまり効率的ではないようです。

三つめ。数えたい文字で文字列を分割して、出来た配列の要素数で数える方法。

int split(String str, char target){
    return str.split(target + "").length - 1;
}

より短かくてちょっとおしゃれっぽいです。 splitメソッドは正規表現を引数に取るので、ドットのような正規表現で意味を持つ文字を探すときには要注意です。

四つめ。正規表現を使ってみた方法。

int regex(String str, char target){
    Pattern p = Pattern.compile(target + "");
    Matcher m = p.matcher(str);
    int count = 0;

    while(m.find()){
        count++;
    }

    return count;
}

長くて格好悪い上に、とても非効率っぽいです。だめだめです。

で、この四つのメソッドに3885文字の文字列を渡して100万回ずつ数えてみました。結果は以下のような感じ。

265文字ヒット101文字ヒット0文字ヒット
simple988ms884ms884ms
replace26,009ms18,685ms10,150ms
split5,983ms3,179ms898ms
regex13,418ms10,237ms8,105ms

なんと、もの凄い差です。 何も考えずにfor文を回すのがベストなようです。 splitを使ったものだとヒットする文字が無いときに速くなるのに、regexではそうでもないというのがちょっと意外でした。splitはコンパイル結果をキャッシュしているのかもですね?

ともかくそんなわけで、難しいことを考えるのはやめましょうという結論でした。