サイズの大きなStringに対して、複数の置換を効率的に行うサンプルです。
オリジナルはそうとう昔に読んだなんかのソースコードですが、記憶が曖昧なのでたぶんそのコードより効率悪いことをしています。かつ、どこかにちゃんとしたものが転がっている気もします。
半分、自分用の備忘です。
String#replaceは毎回置換後のStringインスタンスを生成します。このインスタンスは置換前のStringインスタンスと別物なので、Stringの中身が巨大だったり置換回数が多いとインスタンス生成やGCにかかるコストが問題になります。
以下のコードは「元の文字列を1文字ずつ取り出して」「置換対象か確認し、対象であれば置換して」「結果を納めるStringBuilderへ追加する」処理です。
置換の都度発生するStringインスタンスの生成が抑えられる=コストが下がるという案配です。
// 置換用の情報
String replaceKeys = "&<>"; // 今回はこの3文字が置換対象
ArrayList<String> replaceValues = new ArrayList<String>(3); // 横着
replaceValues.add("&");
replaceValues.add("<");
replaceValues.add(">");
String before = "abc&def<ghi"; // 長い文字列
StringBuilder afterSB = new StringBuilder(); // ちゃんとサイズ指定するほうがベター
// beforeを1文字ずつ取り出して置換
char charAtI;
int replaceIndex;
for (int i = 0; i < before.length(); i++) {
charAtI = before.charAt(i);
replaceIndex = replaceKeys.indexOf(charAtI); // 置換対象かどうかチェック
afterSB.append(replaceIndex < 0 ? charAtI: replaceValues.get(replaceIndex));
}
System.out.println(afterSB.toString());
String#indexOfをbefore(変換元の文字列)でなくreplaceKeys(置換元文字の群)に対して実行しているのがポイントです。自分はとても思いつきません。
なお、置換元の文字(列)が「1文字」でない場合はこの方法は使えません。
その場合は前後の文字も含めて確認しながら読み込む&置換対象チェックしないといけないので、すっげー面倒です。
置換するパターン数にもよりますが、性能品質が許容範囲に収まるのであれば(個人的には)素直にString#replaceを使って可読性を上げるほうを選びます。
コメントを残す