web-dev-qa-db-ja.com

Javaの文字列連結ではなくString.formatを使用する方が良いでしょうか?

JavaでString.formatと文字列連結を使用することに、目に見える違いはありますか?

私はString.formatを使用する傾向がありますが、時折スリップして連結を使用します。一方が他方より優れているかどうか疑問に思っていました。

私の見方では、String.formatは文字列の「フォーマット」をより強力にします。連結すると、誤って余分な%sを入力したり、1つをなくしたりすることを心配する必要がなくなります。

String.formatも短くなります。

どちらが読みやすいかは、頭がどのように機能するかによります。

237
Omar Kooheji

String.format()を使用することをお勧めします。主な理由は、String.format()はリソースファイルから読み込まれたテキストでより簡単にローカライズできるのに対し、連結は言語ごとに異なるコードで新しい実行可能ファイルを生成せずにローカライズできないためです。

アプリをローカライズ可能にすることを計画している場合は、フォーマットトークンの引数位置も指定する習慣を身に付ける必要があります。

"Hello %1$s the time is %2$t"

次に、これをローカライズして、異なる順序を考慮して実行可能ファイルを再コンパイルする必要なく、名前とタイムトークンを交換できます。引数位置を使用すると、同じ引数を関数に2回渡さずに再利用することもできます。

String.format("Hello %1$s, your name is %1$s and the time is %2$t", name, time)
218
workmad3

パフォーマンスについて:

public static void main(String[] args) throws Exception {      
  long start = System.currentTimeMillis();
  for(int i = 0; i < 1000000; i++){
    String s = "Hi " + i + "; Hi to you " + i*2;
  }
  long end = System.currentTimeMillis();
  System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;

  start = System.currentTimeMillis();
  for(int i = 0; i < 1000000; i++){
    String s = String.format("Hi %s; Hi to you %s",i, + i*2);
  }
  end = System.currentTimeMillis();
  System.out.println("Format = " + ((end - start)) + " millisecond");
}

タイミングの結果は次のとおりです。

  • 連結= 265ミリ秒
  • 形式= 4141ミリ秒

したがって、連結はString.formatよりもはるかに高速です。

144
Icaro

パフォーマンスについての議論があるので、StringBuilderを含む比較を追加すると考えました。実際には、連結よりも高速であり、当然ながらString.formatオプションもあります。

これをある種のリンゴとリンゴの比較にするために、私は外部ではなくループ内で新しいStringBuilderをインスタンス化します(これは実際に1つのインスタンス化を行うよりも高速です。 1つのビルダー)。

    String formatString = "Hi %s; Hi to you %s";

    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        String s = String.format(formatString, i, +i * 2);
    }

    long end = System.currentTimeMillis();
    log.info("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        String s = "Hi " + i + "; Hi to you " + i * 2;
    }

    end = System.currentTimeMillis();

    log.info("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        StringBuilder bldString = new StringBuilder("Hi ");
        bldString.append(i).append("; Hi to you ").append(i * 2);
    }

    end = System.currentTimeMillis();

    log.info("String Builder = " + ((end - start)) + " millisecond");
  • 2012-01-11 16:30:46,058 INFO [TestMain]-形式= 1416ミリ秒
  • 2012-01-11 16:30:46,190 INFO [TestMain]-連結= 134ミリ秒
  • 2012-01-11 16:30:46,313 INFO [TestMain]-文字列ビルダー= 117ミリ秒
36
TechTrip

.formatの1つの問題は、静的な型の安全性が失われることです。書式に対して引数が少なすぎる可能性があり、書式指定子に対して間違った型を使用する可能性があります-両方ともIllegalFormatException実行時につながるため、生産を中断するコードをログに記録することになります。

対照的に、+の引数はコンパイラーでテストできます。

34

どちらが読みやすいかは、頭がどのように機能するかによります。

すぐに答えが得られました。

それは個人的な好みの問題です。

文字列の連結はわずかに速いと思いますが、それは無視できるはずです。

16
Thilo

以下に、複数のサンプルサイズをミリ秒単位でテストします。

public class Time {

public static String sysFile = "/sys/class/camera/rear/rear_flash";
public static String cmdString = "echo %s > " + sysFile;

public static void main(String[] args) {

  int i = 1;
  for(int run=1; run <= 12; run++){
      for(int test =1; test <= 2 ; test++){
        System.out.println(
                String.format("\nTEST: %s, RUN: %s, Iterations: %s",run,test,i));
        test(run, i);
      }
      System.out.println("\n____________________________");
      i = i*3;
  }
}

public static void test(int run, int iterations){

      long start = System.nanoTime();
      for( int i=0;i<iterations; i++){
          String s = "echo " + i + " > "+ sysFile;
      }
      long t = System.nanoTime() - start;   
      String r = String.format("  %-13s =%10d %s", "Concatenation",t,"nanosecond");
      System.out.println(r) ;


     start = System.nanoTime();       
     for( int i=0;i<iterations; i++){
         String s =  String.format(cmdString, i);
     }
     t = System.nanoTime() - start; 
     r = String.format("  %-13s =%10d %s", "Format",t,"nanosecond");
     System.out.println(r);

      start = System.nanoTime();          
      for( int i=0;i<iterations; i++){
          StringBuilder b = new StringBuilder("echo ");
          b.append(i).append(" > ").append(sysFile);
          String s = b.toString();
      }
     t = System.nanoTime() - start; 
     r = String.format("  %-13s =%10d %s", "StringBuilder",t,"nanosecond");
     System.out.println(r);
}

}

TEST: 1, RUN: 1, Iterations: 1
  Concatenation =     14911 nanosecond
  Format        =     45026 nanosecond
  StringBuilder =      3509 nanosecond

TEST: 1, RUN: 2, Iterations: 1
  Concatenation =      3509 nanosecond
  Format        =     38594 nanosecond
  StringBuilder =      3509 nanosecond

____________________________

TEST: 2, RUN: 1, Iterations: 3
  Concatenation =      8479 nanosecond
  Format        =     94438 nanosecond
  StringBuilder =      5263 nanosecond

TEST: 2, RUN: 2, Iterations: 3
  Concatenation =      4970 nanosecond
  Format        =     92976 nanosecond
  StringBuilder =      5848 nanosecond

____________________________

TEST: 3, RUN: 1, Iterations: 9
  Concatenation =     11403 nanosecond
  Format        =    287115 nanosecond
  StringBuilder =     14326 nanosecond

TEST: 3, RUN: 2, Iterations: 9
  Concatenation =     12280 nanosecond
  Format        =    209051 nanosecond
  StringBuilder =     11818 nanosecond

____________________________

TEST: 5, RUN: 1, Iterations: 81
  Concatenation =     54383 nanosecond
  Format        =   1503113 nanosecond
  StringBuilder =     40056 nanosecond

TEST: 5, RUN: 2, Iterations: 81
  Concatenation =     44149 nanosecond
  Format        =   1264241 nanosecond
  StringBuilder =     34208 nanosecond

____________________________

TEST: 6, RUN: 1, Iterations: 243
  Concatenation =     76018 nanosecond
  Format        =   3210891 nanosecond
  StringBuilder =     76603 nanosecond

TEST: 6, RUN: 2, Iterations: 243
  Concatenation =     91222 nanosecond
  Format        =   2716773 nanosecond
  StringBuilder =     73972 nanosecond

____________________________

TEST: 8, RUN: 1, Iterations: 2187
  Concatenation =    527450 nanosecond
  Format        =  10291108 nanosecond
  StringBuilder =    885027 nanosecond

TEST: 8, RUN: 2, Iterations: 2187
  Concatenation =    526865 nanosecond
  Format        =   6294307 nanosecond
  StringBuilder =    591773 nanosecond

____________________________

TEST: 10, RUN: 1, Iterations: 19683
  Concatenation =   4592961 nanosecond
  Format        =  60114307 nanosecond
  StringBuilder =   2129387 nanosecond

TEST: 10, RUN: 2, Iterations: 19683
  Concatenation =   1850166 nanosecond
  Format        =  35940524 nanosecond
  StringBuilder =   1885544 nanosecond

  ____________________________

TEST: 12, RUN: 1, Iterations: 177147
  Concatenation =  26847286 nanosecond
  Format        = 126332877 nanosecond
  StringBuilder =  17578914 nanosecond

TEST: 12, RUN: 2, Iterations: 177147
  Concatenation =  24405056 nanosecond
  Format        = 129707207 nanosecond
  StringBuilder =  12253840 nanosecond
14
Derek Ziemba

上記と同じテストで、StringBuildertoString()メソッドを呼び出して変更します。以下の結果は、StringBuilderアプローチが+演算子を使用したString連結よりも少し遅いことを示しています。

ファイル:StringTest.Java

class StringTest {

  public static void main(String[] args) {

    String formatString = "Hi %s; Hi to you %s";

    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
        String s = String.format(formatString, i, +i * 2);
    }

    long end = System.currentTimeMillis();
    System.out.println("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        String s = "Hi " + i + "; Hi to you " + i * 2;
    }

    end = System.currentTimeMillis();

    System.out.println("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();

    for (int i = 0; i < 1000000; i++) {
        StringBuilder bldString = new StringBuilder("Hi ");
        bldString.append(i).append("Hi to you ").append(i * 2).toString();
    }

    end = System.currentTimeMillis();

    System.out.println("String Builder = " + ((end - start)) + " millisecond");

  }
}

シェルコマンド:(StringTestを5回コンパイルして実行)

> javac StringTest.Java
> sh -c "for i in \$(seq 1 5); do echo \"Run \${i}\"; Java StringTest; done"

結果:

Run 1
Format = 1290 millisecond
Concatenation = 115 millisecond
String Builder = 130 millisecond

Run 2
Format = 1265 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond

Run 3
Format = 1303 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond

Run 4
Format = 1297 millisecond
Concatenation = 114 millisecond
String Builder = 127 millisecond

Run 5
Format = 1270 millisecond
Concatenation = 114 millisecond
String Builder = 126 millisecond
9
Akos Cz

String.format()は単なる文字列の連結以上のものです。たとえば、String.format()を使用して特定のロケールで数値を表示できます。

ただし、ローカライズを気にしないのであれば、機能的な違いはありません。たぶん一方は他方よりも速いかもしれませんが、ほとんどの場合、無視できます。

6
Fortega

通常、String.formatよりも文字列の連結を優先する必要があります。後者には2つの主な欠点があります。

  1. ローカルに構築される文字列をエンコードしません。
  2. 構築プロセスは文字列にエンコードされます。

ポイント1では、String.format()呼び出しが1つの順次パスで何をしているのか理解できないことを意味します。引数の位置を数えながら、フォーマット文字列と引数の間を行き来しなければなりません。短い連結の場合、これはそれほど大きな問題ではありません。ただし、これらの場合、文字列の連結はそれほど冗長ではありません。

ポイント2では、構築プロセスの重要な部分がformat string(DSLを使用)でエンコードされていることを意味します。文字列を使用してコードを表すには、多くの欠点があります。それは本質的にタイプセーフではなく、構文の強調表示、コード分析、最適化などを複雑にします。

もちろん、Java言語の外部のツールまたはフレームワークを使用する場合、新しい要因が作用する可能性があります。

4

目に見える違いがあるかもしれません。

String.formatは非常に複雑で、その下に正規表現を使用します。したがって、どこでも使用することを習慣にせず、必要な場所でのみ使用してください。

StringBuilderは、桁違いに高速です(ここで誰かが既に指摘したように)。

2
Pawel Zieminski

特定のベンチマークを行ったことはありませんが、連結のほうが速いと思います。 String.format()は新しいFormatterを作成し、これは新しいStringBuilder(サイズは16文字のみ)を作成します。特に長い文字列をフォーマットしていて、StringBuilderがサイズ変更を続けている場合、これはかなりのオーバーヘッドです。

ただし、連結はあまり有用ではなく、読みにくくなります。いつものように、どちらが優れているかを確認するには、コードのベンチマークを行う価値があります。リソースバンドル、ロケールなどがメモリにロードされ、コードがJIT化された後、サーバーアプリでは違いが無視できる場合があります。

ベストプラクティスとして、適切なサイズのStringBuilder(Appendable)とLocaleを使用して独自のFormatterを作成し、多くの書式設定を行う場合はそれを使用することをお勧めします。

2
AngerClown

読みやすさとパフォーマンスの両方の面で優れているはずなので、MessageFormat.formatを使用することもできます。

上記の回答でIcaroで使用したものと同じプログラムを使用し、パフォーマンス変数を説明するためにMessageFormatを使用するためのコードを追加して強化しました。

  public static void main(String[] args) {
    long start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = "Hi " + i + "; Hi to you " + i * 2;
    }
    long end = System.currentTimeMillis();
    System.out.println("Concatenation = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = String.format("Hi %s; Hi to you %s", i, +i * 2);
    }
    end = System.currentTimeMillis();
    System.out.println("Format = " + ((end - start)) + " millisecond");

    start = System.currentTimeMillis();
    for (int i = 0; i < 1000000; i++) {
      String s = MessageFormat.format("Hi %s; Hi to you %s", i, +i * 2);
    }
    end = System.currentTimeMillis();
    System.out.println("MessageFormat = " + ((end - start)) + " millisecond");
  }

連結= 69ミリ秒

形式= 1435ミリ秒

MessageFormat = 200ミリ秒

更新:

SonarLintレポートによると、Printfスタイルのフォーマット文字列を正しく使用する必要があります(squid:S3457)

Printfスタイルのフォーマット文字列は、コンパイラによって検証されるのではなく、実行時に解釈されるため、誤った文字列が作成されるというエラーが含まれる場合があります。このルールは、Java.util.Formatter、Java.lang.String、Java.io.PrintStream、MessageFormat、およびJava.ioのformat(...)メソッドを呼び出すときに、printfスタイルのフォーマット文字列と引数との相関関係を静的に検証します。 .PrintWriterクラスおよびJava.io.PrintStreamまたはJava.io.PrintWriterクラスのprintf(...)メソッド。

Printfスタイルを中括弧に置き換えて、以下のような興味深い結果を得ました。

連結= 69ミリ秒
形式= 1107ミリ秒
形式:中括弧= 416ミリ秒
MessageFormat = 215ミリ秒
MessageFormat:curly-brackets = 2517ミリ秒

私の結論:
上で強調したように、中括弧でString.formatを使用することは、優れた読みやすさとパフォーマンスのメリットを得るための良い選択です。

0

上記のプログラムでは、文字列連結とString.Formatを比較できません。

以下のように、コードブロックでString.FormatとConcatenationを使用する位置を交換することもできます。

public static void main(String[] args) throws Exception {      
  long start = System.currentTimeMillis();

  for( int i=0;i<1000000; i++){
    String s = String.format( "Hi %s; Hi to you %s",i, + i*2);
  }

  long end = System.currentTimeMillis();
  System.out.println("Format = " + ((end - start)) + " millisecond");
  start = System.currentTimeMillis();

  for( int i=0;i<1000000; i++){
    String s = "Hi " + i + "; Hi to you " + i*2;
  }

  end = System.currentTimeMillis();
  System.out.println("Concatenation = " + ((end - start)) + " millisecond") ;
}

ここでFormatがより高速に動作することに驚かれることでしょう。これは、作成された初期オブジェクトが解放されない可能性があり、メモリの割り当てに問題があり、それによりパフォーマンスが低下する可能性があるためです。

0
DotNetUser

String.Formatに慣れるには少し時間がかかりますが、ほとんどの場合、それだけの価値があります。 NRA(何も繰り返さない)の世界では、トークン化されたメッセージ(ログまたはユーザー)を定数ライブラリ(静的クラスに相当するものを好む)に保持し、必要に応じてString.Formatでそれらを呼び出すことは非常に便利ですローカライズされているかどうか。このようなライブラリを連結方法で使用しようとすると、連結を必要とするあらゆるアプローチで、読み取り、トラブルシューティング、校正、管理が難しくなります。交換はオプションですが、パフォーマンスが良いとは思えません。何年も使用した後、String.Formatでの最大の問題は、呼び出しの長さが別の関数(Msgなど)に渡されるときに不便に長いことですが、エイリアスとして機能するカスタム関数で簡単に回避できます。

0
user665056