サイズの大きなStringに対する置換処理(Java)

サイズの大きなStringに対して、複数の置換を効率的に行うサンプルです。

オリジナルはそうとう昔に読んだなんかのソースコードですが、記憶が曖昧なのでたぶんそのコードより効率悪いことをしています。かつ、どこかにちゃんとしたものが転がっている気もします。
半分、自分用の備忘です。

String#replaceは毎回置換後のStringインスタンスを生成します。このインスタンスは置換前のStringインスタンスと別物なので、Stringの中身が巨大だったり置換回数が多いとインスタンス生成やGCにかかるコストが問題になります。

以下のコードは「元の文字列を1文字ずつ取り出して」「置換対象か確認し、対象であれば置換して」「結果を納めるStringBuilderへ追加する」処理です。
置換の都度発生するStringインスタンスの生成が抑えられる=コストが下がるという案配です。

// 置換用の情報
String replaceKeys = "&<>"; // 今回はこの3文字が置換対象
ArrayList<String> replaceValues = new ArrayList<String>(3); // 横着
replaceValues.add("&amp;");
replaceValues.add("&lt;");
replaceValues.add("&gt;");

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を使って可読性を上げるほうを選びます。

コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください