背景
「Java で CamelCase(単語の区切りで大文字にする記法)と snake_case(単語の区切りで_を使って連結する記法)を相互変換するにはどうしたらよいか?」という話題が会社で出ました。Java の標準ライブラリにはこの機能がないので、意外に必要とされないのでしょうか?
CaseFormat
調べてみたところ、Google Guava にこの変換をやるためのクラス CaseFormat があることを知りました。元の文字列のフォーマットを指定して、to メソッドで変換先のフォーマットを指定する、という感じで使うようです。なお、今回私の言う snake_case は LOWER_UNDERSCORE という名前になっています。
CamelCase から snake_case への変換は下記のコードで可能です。
final String snake = CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, "TomatoCurry");
逆に、snake_case から CamelCase への変換は下記の通りです。
final String camel = CaseFormat.LOWER_UNDERSCORE.to(CaseFormat.UPPER_CAMEL, "tomato_curry");
簡単でわかりやすいですね。Guava はライセンスも Apache License 2.0 ですし、通常はこちらを使っておけば間違いないでしょう。
自作してみる
ただ、1機能を使いたいためだけに Guava を導入するのも何だと思ったので、自分で似た機能を勉強も兼ねて実装してみました。
ソースコード
全体はこちら
public static final String camelToSnake(final String camel) {
if (StringUtils.isEmpty(camel)) {
return camel;
}
final StringBuilder sb = new StringBuilder(camel.length() + camel.length());
for (int i = 0; i < camel.length(); i++) {
final char c = camel.charAt(i);
if (Character.isUpperCase(c)) {
sb.append(sb.length() != 0 ? '_' : "").append(Character.toLowerCase(c));
} else {
sb.append(Character.toLowerCase(c));
}
}
return sb.toString();
}
public static final String snakeToCamel(final String snake) {
if (StringUtils.isEmpty(snake)) {
return snake;
}
final StringBuilder sb = new StringBuilder(snake.length() + snake.length());
for (int i = 0; i < snake.length(); i++) {
final char c = snake.charAt(i);
if (c == '_') {
sb.append((i + 1) < snake.length() ? Character.toUpperCase(snake.charAt(++i)) : "");
} else {
sb.append(sb.length() == 0 ? Character.toUpperCase(c) : Character.toLowerCase(c));
}
}
return sb.toString();
}
渡された文字列を1字1字見て、変換対象の文字列があったら処理をして StringBuilder に append し、最後に新しい文字列オブジェクトを生成して返す、という単純な処理をするメソッドです。
比較・検証
せっかくなので実行速度を計測してみることにしました。検証用のコードはこちらです。なお、Collection フレームワークには GS Collections を使いました。
Method | Average time[ms] |
---|---|
Guava | 450.2 |
CharAt | 277.0 |
Array | 225.6 |
自作の方は雑な機能しかない分だけ速く実行できているようです。
【追記】 JMH でのパフォーマンスチェック
その後、Java のコードのパフォーマンスチェックは JMH を使うべきだと知りましたので、改めて JMH でやり直しました。
修正差分
JMH を使うための修正差分は下記の commit をご覧ください。
実行環境
Key | Value |
---|---|
Java SE | 1.8.0_u121 |
JMH | 1.18 |
Gradle | 3.4.1 |
OS | Windows 10 |
CPU | Intel(R) Core(TM) i7-7500U CPU @ 2.70GHz 2.90 GHz |
RAM | 16.0GB |
JMH の設定
Key | Value |
---|---|
jmhVersion | 1.18 |
warmupIterations | 5 |
iterations | 20 |
fork | 2 |
benchmarkMode | ['thrpt'] |
failOnError | true |
実行結果
# Run complete. Total time: 00:02:40
Benchmark Mode Cnt Score Error Units
SnakeCamelBenchmark.convertGuava thrpt 40 32.391 ± 6.586 ops/ms
SnakeCamelBenchmark.convertCharAt thrpt 40 37.823 ± 4.994 ops/ms
SnakeCamelBenchmark.convertArray thrpt 40 27.984 ± 2.503 ops/ms
結論
Guava の CaseFormat クラスを使いましょう。