String#formatの実力

はてなに登録して、記念すべき最初の投稿がこんなものでいいのだろうか・・・・。

ちょっと古いが、文字列を整形するために、Java1.5で使えるようになったString#formatの速度検証を行ってみた。

今回の検証は以下の通り
「1ブロック10バイトの半角スペースを10000000個ファイルに出力する。」

10バイトの半角スペースを以下の4通りの方法で実装した。

  1. あらかじめ10バイトのスペースを定数で用意
  2. 毎回10バイトのスペースを変数で生成して用意
  3. StringBufferクラスでスペースをappendする処理を10回ループさせて用意
  4. String#formatを使用(フォーマット:%10s)して10バイトのスペースの変数を用意


測定結果は以下のとおり。

No 方式 所要時間
1 定数 1520ms
2 変数 1471ms
3 StringBuffer使用 5351ms
4 String#format使用 33490ms


String#formatおそっ!!!!


参考までに今回作成したソースを載せておく。

package sample;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Date;


/**
 * String#formatのパフォーマンスを測定するクラス
 * @author @labo
 *
 */
public class StringFormatPerformanceChecker {
	/* ループ回数 */
	private static final int COUNT = 10000000;
	/* 10バイトのスペース定数 */
	private static final String STR = "          ";

	/**
	 * メインメソッド
	 * @param args 使用しません
	 */
	public static void main(String[] args) {
		StringFormatPerformanceChecker p = new StringFormatPerformanceChecker();

		System.out.println("static : " + p.new StaticChecker("static").getTime());
		System.out.println("variable : " + p.new VariableChecker("variable").getTime());
		System.out.println("dynamic : " + p.new DynamicChecker("dynamic").getTime());
		System.out.println("string format : " + p.new StringFormatChecker("stringformat").getTime());
	}

	/**
	 * パフォーマンスチェックの抽象クラス
	 * @author @labo
	 *
	 */
	private abstract class Checker {
		/* ファイル出力オブジェクト */
		private BufferedWriter bw = null;

		/**
		 * コンストラクタ
		 *
		 * @param fileName ファイル名(ファイル名 + 「.txt」でファイル名となる)
		 */
		public Checker(String fileName) {
			try {
				bw = new BufferedWriter(new FileWriter(new File("d:/" + fileName + ".txt")));
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		/**
		 * 時間を計測するクラス
		 * @return 経過時間(ms)
		 */
		public long getTime() {
			// 開始時刻
			Date start = new Date();
			try {
				for (int i=0; i<COUNT; i++) { // 10000000回ループ
					// 10バイトのスペースを出力
					bw.write(getString());
				}
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					bw.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			// 終了時刻
			Date end = new Date();

			// 終了時刻 - 開始時刻をlongで返却(経過ms)
			return end.getTime() - start.getTime();
		}

		/**
		 * 10バイトのスペース取得処理
		 * このメソッドを各々の方式で実装する
		 * @return 10バイトのスペース文字列
		 */
		protected abstract String getString();
	}


	/**
	 * 定数を用いたパフォーマンス測定クラス
	 * @author @labo
	 *
	 */
	private class StaticChecker extends Checker {

		/**
		 * コンストラクタ
		 * @param fileName
		 * @see Checker
		 */
		public StaticChecker(String fileName) {
			super(fileName);
		}

		/**
		 * 定数文字列を返却する
		 */
		@Override
		protected String getString() {
			return STR;
		}

	}

	/**
	 * 変数を用いたパフォーマンス測定クラス
	 * @author @labo
	 *
	 */
	private class VariableChecker extends Checker {

		/**
		 * コンストラクタ
		 * @param fileName
		 * @see Checker
		 */
		public VariableChecker(String fileName) {
			super(fileName);
		}

		/**
		 * 変数文字列を返却する
		 */
		@Override
		protected String getString() {
			return "          ";
		}

	}

	/**
	 * StrngBufferを用いたパフォーマンス測定クラス
	 * @author @labo
	 *
	 */
	private class DynamicChecker extends Checker {

		/**
		 * コンストラクタ
		 * @param fileName
		 * @see Checker
		 */
		public DynamicChecker(String fileName) {
			super(fileName);
		}

		/**
		 * StringBufferで文字列を生成して返却する
		 */
		@Override
		protected String getString() {
			StringBuffer sb = new StringBuffer();
			for (int j=0; j<10; j++) {
				sb.append(" ");
			}
			return sb.toString();
		}

	}

	/**
	 * String#formatを用いたパフォーマンス測定クラス
	 * @author @lLabo
	 *
	 */
	private class StringFormatChecker extends Checker {

		/**
		 * コンストラクタ
		 * @param fileName
		 * @see Checker
		 */
		public StringFormatChecker(String fileName) {
			super(fileName);
		}

		/**
		 * String#formatで文字列を生成して返却する
		 */
		@Override
		protected String getString() {
			return String.format("%10s", "");
		}

	}

}