3

最初のステップで、次のコードを実行します。

public class Demo  {
    public static void main(String[] args) {
        String x = "x";
        long start = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++)

        {

            x = x.concat("s");

            // x+="k";

        }

        System.out.println(System.currentTimeMillis() - start);
    }

}

アウト: 13579。

2 番目のステップで、次のコードを実行します。

public class Demo {
    public static void main(String[] args) {
        String x = "x";
        long start = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++)

        {

            //x = x.concat("s");

             x+="k";

        }

        System.out.println(System.currentTimeMillis() - start);
    }

}

アウト: 27328.

2 つの質問があります。

  1. 私の銀行印と言えますか?
  2. (+) と concat() のタイムラインに大きな違いがあるのはなぜですか??? 13.5秒 VS 27秒 なんで?
4

5 に答える 5

6

あなたのマイクロベンチマークは問題ないと思います。結果を再現できます。

私のJVMでは、x += "k"2倍遅い理由は、内部で次のことを行っているためです。

  1. 新しいを作成しStringBuilderます。
  2. に追加xStringBuilderます。
  3. に追加"k"StringBuilderます。
  4. を呼び出しStringBuilder.toString()て、結果を に代入しxます。

これにより、文字データが 2 回 (手順 2 で 1 回、手順 4 で 1 回) コピーされます。

一方、x = x.concat("s")データを一度だけコピーします。

この二重コピーによりx += "k"、他のバージョンよりも 2 倍遅くなります。

興味がある方は、コンパイラが+=ループ用に生成したバイトコードを以下に示します。

   10:  goto    36
   13:  new #24; //class java/lang/StringBuilder
   16:  dup
   17:  aload_1
   18:  invokestatic    #26; //Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   21:  invokespecial   #32; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   24:  ldc #35; //String k
   26:  invokevirtual   #37; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   29:  invokevirtual   #41; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   32:  astore_1
   33:  iinc    4, 1
   36:  iload   4
   38:  ldc #45; //int 100000
   40:  if_icmplt   13

命令 21 と 29 は、2 つのコピーが作成される場所です。

于 2011-10-23T19:47:47.910 に答える
0

これらが同等であることを知っていれば役立ちますか:

x+="k";
x = new StringBuffer(x).append("k").toString();

または、新しいJava(1.5か1.6かは忘れました)では、代わりにこれを使用します。少なくとも、それに加えられたロックを回避しStringBufferます。

x = new StringBuilder(x).append("k").toString();

すべてのメモリ管理が行われているため、メソッドがより適切に機能することは不思議ではありconcat(String)ません。一方、そのループを実行する場合は、可変オブジェクトと不変オブジェクトの間をあまり変換しない方がよいでしょう。

String x = "x";
long start = System.currentTimeMillis();

StringBuilder sb = new StringBuilder(x);
for (int i = 0; i < 100000; i++) {
    sb.append("f");
}
x = sb.toString();

System.out.println(System.currentTimeMillis() - start);

StringBuilder(これStringBufferもまた、他の理由で効率的ではありませんが) インテリジェントなバッファー管理を行うため、これは非常に高速な手法になります。それよりもさらに高速にするには、実際の努力が必要です (または、少なくともバッファーのサイズを事前に調整する必要があります。この場合は可能ですが、一般的にはより困難です)。

于 2011-10-23T19:55:47.733 に答える
0

次のような行をシングルステップで進むと

    String x2 = x + "x";

Eclipse ではStringBuilder 、新しいString. それがあなたの2番目のケースで起こっていることです。

最初のケースでは、何もせずString.concatに新しいものを直接作成するため、少し高速です。StringStringBuilder

于 2011-10-23T19:56:34.947 に答える
0

これを試してみてください。あなたの両方を打ち負かします:

public class Demo {
    public static void main(String[] args) {
        StringBuilder x = new StringBuilder("x");
        long start = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++)

        {

             x.append("k");

        }

        System.out.println(System.currentTimeMillis() - start);
    }

}

そして、これがわずかに最適化されたバージョンです。

public class Demo {
    public static void main(String[] args) {
        StringBuilder x = new StringBuilder(100001).append('x');
        long start = System.currentTimeMillis();

        for (int i = 0; i < 100000; i++)

        {

             x.append('k');

        }

        System.out.println(System.currentTimeMillis() - start);
    }

}
于 2011-10-23T19:53:19.677 に答える
0

を使用して文字列を連結する+と、実際には新しい StringBuffer を作成し、元の文字列の代わりにそれを操作するため、最終的に concat のみを使用する場合よりも遅くなる可能性があります。

正確性に関しては、実際に何が起こっているかを確認するためにバイトコードを確認することをお勧めします。コンパイラは、実行の正確な結果がわかっている場合、一部のコードを最適化して除外することがあり、それが変更されることはありません。

于 2011-10-23T19:43:26.033 に答える