2012年12月23日日曜日

Javaでbyte配列を16進文字列にする時の速度比較

Javaでプログラミングしていると、byte配列(byte[])を16進の文字列に変換したいことは多い。
例えば、MD5やSHA-1,SHA-512などハッシュ値に置き換えるときや、バイナリデータのバンプなどがある。
上記のコードは、様々なところで紹介されているが、いったいどの方法が速いのだろうか?

VM環境は、Windows 7 + Oracle JRE 7 という、とても一般的な環境。
試したパターンは、10通り。そのうち比較として面白いものを4つ選んだ。
どのパターンも前提として、by:byte[]、sb:StringBuilder、とする。

//パターン1
for (byte b : by)
 sb.append(String.format("%02x", b));
//パターン2
for (int b : by) {
 sb.append(Integer.toHexString(b >> 4 & 0xF));
 sb.append(Integer.toHexString(b & 0xF));
}
//パターン3
for (int b : by) {
 int b2 = b & 0xff;
 if (b2 < 16) sb.append("0");
 sb.append(Integer.toHexString(b2));
}
//パターン4
for (int b : by) {
 sb.append(Character.forDigit(b >> 4 & 0xF, 16));
 sb.append(Character.forDigit(b & 0xF, 16));
}
実行結果は、以下のとおりである。
パターン実行時間
パターン147817ms
パターン24313ms
パターン32310ms
パターン4588ms
パターン1は、フォーマット文字列の解析があるので、明らかに遅くなるのは間違いない。ほぼ予想通りであるといえる。 パターン2とパターン3からいえることは、これらの処理の中でInteger.toHexString()が一番時間がかかるといえる。 結果としては、パターン4が明らかに速い。理由はOpenJDKのソースを見ると明らかだ。 Integer.toHexStringは複数の桁を検査し、それをnew String()している。 Character.forDigitは1桁目のみ検査し、それをcharで返している。 charはintと同様クラスではないので速いのだろう。Stringはクラスなので、生成にコストがかかる。 今回の調査でわかったことは、Stringの生成は意外とコストが大きく、StringBuilder.append()は速いということだった。 Stringの生成が遅いので作られたものがStringBuilderという点からしても当たり前か。

Enterprise JavaBeans 3.1 第6版