JMHを使用して有望なアプローチをテストしました。完全なベンチマークコード。
テスト中の仮定(毎回コーナーケースをチェックしないようにするため):入力文字列の長さが常に1より大きい。
結果
Benchmark Mode Cnt Score Error Units
MyBenchmark.test1 thrpt 20 10463220.493 ± 288805.068 ops/s
MyBenchmark.test2 thrpt 20 14730158.709 ± 530444.444 ops/s
MyBenchmark.test3 thrpt 20 16079551.751 ± 56884.357 ops/s
MyBenchmark.test4 thrpt 20 9762578.446 ± 584316.582 ops/s
MyBenchmark.test5 thrpt 20 6093216.066 ± 180062.872 ops/s
MyBenchmark.test6 thrpt 20 2104102.578 ± 18705.805 ops/s
スコアは1秒あたりの操作数であり、多いほど優れています。
テスト
test1
アンディとヒリンクの最初のアプローチでした。
string = Character.toLowerCase(string.charAt(0)) + string.substring(1);
test2
アンディの2番目のアプローチでした。それもIntrospector.decapitalize()
ダニエルによって提案されましたが、2つのif
ステートメントはありません。最初if
はテストの仮定のために削除されました。2つ目は正しさに違反していたため削除されました(つまり、入力"HI"
はを返します"HI"
)。これはほぼ最速でした。
char c[] = string.toCharArray();
c[0] = Character.toLowerCase(c[0]);
string = new String(c);
test3
の変更でしたtest2
が、の代わりにCharacter.toLowerCase()
32を追加しました。これは、文字列がASCIIの場合にのみ正しく機能します。これが最速でした。c[0] |= ' '
マイクのコメントから同じパフォーマンスを与えた。
char c[] = string.toCharArray();
c[0] += 32;
string = new String(c);
test4
中古StringBuilder
です。
StringBuilder sb = new StringBuilder(string);
sb.setCharAt(0, Character.toLowerCase(sb.charAt(0)));
string = sb.toString();
test5
2つのsubstring()
呼び出しを使用しました。
string = string.substring(0, 1).toLowerCase() + string.substring(1);
test6
リフレクションを使用してchar value[]
文字列を直接変更します。これが最も遅かった。
try {
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] value = (char[]) field.get(string);
value[0] = Character.toLowerCase(value[0]);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
結論
文字列の長さが常に0より大きい場合は、を使用しますtest2
。
そうでない場合は、コーナーケースを確認する必要があります。
public static String decapitalize(String string) {
if (string == null || string.length() == 0) {
return string;
}
char c[] = string.toCharArray();
c[0] = Character.toLowerCase(c[0]);
return new String(c);
}
テキストが常にASCIIであることが確実であり、このコードがボトルネックにあるために極端なパフォーマンスを探している場合は、を使用してくださいtest3
。