いつの間にかStringの足し算でも構わないケースが出てきていた話

Table of Contents

こんにちは、殿内(@tonoccho)です

先日、Twitterで以下のようなことを書きました。

割とStringBufferとStringBuilderの使い分けをパフォーマンスだけで決めてる人多いなぁ。スレッドセーフかどうかについても考えた方が良いが、文字列をマルチスレッドでごにょる事が果たしてあるかと言うのはあるけど絶対ないとは言わないから。ローカル変数ならStringBuilder一択なんだろうけどね。

— とのっちょ (@tonoccho) August 14, 2019

当初の思いとしてはStringBuilderがスレッドセーフのためのオーバーヘッド少ない分早いから、StringBufferじゃなくてStringBuilderを選びましょう、という人がなんか多いみたいで、スレッドセーフじゃなくなっていることを考えるのも必要なんじゃないかなー、ようは「StringBuilder一択でしょ!」っていう単純化した考えってどうよ?というおっさん臭い意見だったんです。

StringBuilderにせよStringBufferにせよ、基本的にはメソッド内のローカル変数で使われるものだからそこまでナーバスにならなくてもいいとは思いますが、この視点がかけたがゆえにへんてこな文字列が出来上がるようなケースがあるんじゃないのかな?くらいの話でしたが、その後Javaチャンピオンおふた方含めた豪の猛者達が参加される大惨事となり結果として新たな治験を得られたのでツイッターってすごい、というかJava-Jaってやっぱり怖いというか。

Stringの足し算はコンパイラによってStringBuilderのメソッドチェインに置き換えられる

仮に、以下のようなコードを書いたとします。

String str = "Java-jaから";
String str2 = "きますた";
String str3 = str + str2;

昔からやっている人がこのコード見たら一瞬にして「StringBuilder使って書き直せ、今すぐにだ」というレビューになると思います。というのも、Stringの足し算は昔は結構重たい処理で、strを作って、str2を作って、改めてstr3を作ってそこにstrとstr2を入れる、というような動きをしていたからです。だったらなんでStringの足し算なんか入れたんだよと思わなくもないんですが、これが今はコンパイラによってStringBuilderのメソッドチェインに置き換えられるんだそうです。

StringBufferはメソッドがsynchronize宣言されてるだけなので、それでスレッドセーフになるシチュエーションは基本的にないと思ってよく、適切に同期の設計しないといけない。

なので古いStringBufferは古いAPI互換以外では忘れていいと思う。 https://t.co/5BbF3Ec387

— なぎせ ゆうき (@nagise) August 14, 2019

この辺からたどるといいでしょう。

なんにしてもStringの足し算のパフォーマンスについては結構な改善が図られているようです。

ただし最適化がどうなるかは知っておいたほうが良い

Stringの足し算が何でもかんでもStringBuilderに置き換わるかというと別にそんなことも無いようで、ワンライナーで書き切れるならそれでいいけど、そこかしこでStringを足し続けるなら今でもStringBuilderがいいようです。ようはStringオブジェクトが作られたら負け、という感じでしょうか。

Java11からはまた違った高速化が入る

StringBuilderもあまり使われなくなるような気も... https://t.co/4Yb0wERMZG

— Yuichi Sakuraba (@skrb) August 14, 2019

さくらばさんにStringBuilderもこれからは使われなくなるのでは?と意見を頂いてからの議論だったんですが、Java11では更にStringBuilderではなくinvokedynamicによる文字列の結合、というところに成るようです。不勉強でこれがどういうものかはガッツリ理解していないんですが、埋め込んだツイートのリンク先によれば、バイトコードになったときに命令数が減っているようです。命令数が減れば早くなるととりあえず単純に考えています。invokedynamic自体はJava7くらいからあったようです。

混乱を避けるためという意味でStringBuilderを使い続けるという実務的なあれはありそう

なんにしてもStringの足し算が内部的にStringBuilderに変換されるということを知っている人はStringの足し算を、そうでない人はStringBuilderを、というふうにしてしまうと全体的に整合性が取れないばかりか「なんでStringの足し算してんだゴルァ!」っていう人に説明するコストも面倒くさいから結局StringBuilderを使い続けるかもしれません(実際Java11にならないとinvokedynamicの効果は出ないみたいだし)。

新しい知見をありがとうございました!

キチンとテーマを持ってツイッターを使うと本当に有益ですね!

技術の最新記事

プログラミングの最新記事