はじめに
WebサービスやAndroidスマートフォンのアプリ開発で何かと使うJava言語。Java VMで動作する中間言語にコンパイルされるものの、なんか動作が遅いと感じることがあります。
Native(C言語やC++言語)に比べると、VM上で動作するので多少パフォーマンス理論値は劣りますが、
パフォーマンスの問題は、Java言語や実行環境にあるのではなく、プログラムの書き方にある。
こんな方におすすめ
- Javaを使ったソフトウェアのパフォーマンスを改善したい
- 要求されている性能を満たせず、何から始めればよいか分からない
今回は、パフォーマンスを大幅に向上させることが期待できる優れたコーディング習慣が紹介されている以下の著書を参考に、実際にJavaのプログラミングでよくある事例を紹介いたします。
ループの条件式は変数を使用する
例えば、以下のような for ループを記述している場合、条件式の無駄な計算が発生しています。
1 2 3 4 |
for (int i = 0; i < list.size(); i++) { // ... } |
ループの条件式に複雑な式を使用してはいけません。(上記例の「list.size()」が該当)
ループでは、ループの条件式が繰り返し評価・計算されます。
複雑な式を使用せず、条件式を変数にすることでプログラムがより速く実行されるようになります。
パフォーマンスを改善する記述例
1 2 3 4 |
for (int i = 0, len = list.size(); i < len; i++) { // ... } |

乗算・除算にはシフト演算子を使用する
2、4、8、などで乗算・除算する計算式は、シフト演算子を使用すると計算が早くなります。

乗算・除算の悪い例
1 2 3 4 5 6 7 8 |
int num = a * 2; int num = a * 4; int num = a * 8; int num = a / 2; int num = a / 4; int num = a / 8; |
乗算・除算の記述改善例
1 2 3 4 5 6 7 8 |
int num = a << 1; // a * 2 int num = a << 2; // a * 4 int num = a << 3; // a * 8 int num = a >> 1; // a / 2 int num = a >> 2; // a / 4 int num = a >> 3; // a / 8 |
final 修飾子をできるだけ使用する
final 修飾子をつけたクラスやメソッドは、継承してオーバーライドできなくなります。クラスに final 修飾子をつけた場合は、そのクラスの全てのメソッドが final になり派生することができなくなります。
なぜ final 修飾子をつけるとパフォーマンスが改善するのか?
ここはコンパイラの気持ちになって考えるとイメージしやすいです。
final がない場合は「オーバーライドされる可能性」があるため、常にどのクラスのメソッドを呼び出すべきかを考える必要があります。つまり、インライン化することができずメソッドとしてコンパイルされます。
対して、final が付いている場合は、常にインライン化する機会を探しながらコンパイルされるため、効率の良いバイトコードが生成されるわけです。

文字列結合には StringBuilder を使う
文字列を結合する場合、Javaでは以下の方法があります。
- 文字列結合演算子(+)
- String.concat() による結合
- StringBuilder による結合
- StringBuffer による結合
たいていの場合、数個の文字列を一つの文字列に結合できる演算子(+)を使用していると思います。
しかし、+演算子を使用する場合は、内部でオブジェクト生成が発生するため遅くなります。
文字列結合の悪い例
1 2 3 4 5 6 7 8 |
public String getParamsString() { String result = ""; for (int i = 0; i < numParams(); i++) { result += getParam(i); // String の結合 } return result; } |
+演算子による文字列結合では、文字列を結合させる度に新しい文字列オブジェクトを作ります。
String型のオブジェクトはイミュータブル(immutable:変更不可)なので、コピーを行うためです。
注意ポイント
オブジェクトの生成にはコストがかかってパフォーマンスを悪化させますし、生成したオブジェクトの残骸が増えるとGC(ガベージコレクション)が動作してさらに性能を悪化させます。
StringBuilderを使用した改善例
1 2 3 4 5 6 7 8 |
public String getParamsString() { StringBuilder b = new StringBuilder(numParams() * MAX_LEN); for (int i = 0; i < numParams(); i++) { b.append(getParam(i)); } return b.toString(); } |
注意ポイント
パフォーマンスを向上させるためには、自動的にバッファサイズを伸長させないよう、結合結果を保持できる十分なサイズの StringBuilder を使用前に割り当てておく必要があります。

おわりに
いかがでしたでしょうか。
理解はしているものの、普段のプログラミングで意識してコーディングすることは難しいです。
性能が悪いと感じたら、先ずはプログラムを見直してみましょう。
たいていは、Java VMやフレームワークではなく、Javaのプログラム自体に問題があります。