1. ホーム

[解決済み】ArrayListを文字列に変換する最適な方法

2022-03-26 05:05:23

質問

私は ArrayList を完全にStringとして出力したい。基本的に、私はそれを toString の各要素をタブで区切ってください。これを行うための高速な方法はありますか?ループして(または各要素を削除して)Stringに連結することができますが、これは非常に遅いと思います。

どのように解決するのですか?

Java 8 では String.join(separator, list) メソッドを参照してください。 Vitalii Federenkoの回答 .

Java 8以前は、ループを使って ArrayList が唯一の選択肢でした。

このコードが望ましくない理由と、代わりにどのコードを使用すべきかを見るために、この回答の一番下まで読み進めてください。

ArrayList<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");

String listString = "";

for (String s : list)
{
    listString += s + "\t";
}

System.out.println(listString);

実際には、文字列の連結で問題ないでしょう。 javac コンパイラは、文字列の連結を一連の append 演算を行うことができます。 StringBuilder はとにかく 以下はバイトコードを分解した一部です。 for のループに入る。

   61:  new #13; //class java/lang/StringBuilder
   64:  dup
   65:  invokespecial   #14; //Method java/lang/StringBuilder."<init>":()V
   68:  aload_2
   69:  invokevirtual   #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   72:  aload   4
   74:  invokevirtual   #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   77:  ldc #16; //String \t
   79:  invokevirtual   #15; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   82:  invokevirtual   #17; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;

見てわかるように、コンパイラはこのループを最適化するために StringBuilder ということで、パフォーマンスはあまり気にする必要はないでしょう。

(OK、ぱっと見は StringBuilder はループの繰り返しごとにインスタンス化されるので、最も効率的なバイトコードとは言えないかもしれません。インスタンス化し、明示的に StringBuilder の方が、おそらく良いパフォーマンスが得られるでしょう)。

実際、文字列の連結のパフォーマンスを気にするよりも、何らかの出力(ディスクであれ画面であれ)をしたほうが、少なくとも一桁は遅くなると思います。

編集 コメントで指摘されているように、上記のコンパイラの最適化は、確かに新しいインスタンスの StringBuilder を各反復で実行します。(これは以前にも指摘しました)。

による応答が最も最適化された手法となります。 ポール・トンブリン をインスタンス化するだけなので StringBuilder オブジェクトの外側で for のループになります。

上記のコードにリライトして

ArrayList<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");

StringBuilder sb = new StringBuilder();
for (String s : list)
{
    sb.append(s);
    sb.append("\t");
}

System.out.println(sb.toString());

のみをインスタンス化します。 StringBuilder を2回呼び出すだけです。 append メソッドのインスタンス化を示しています。 StringBuilder とループがある)。

   // Instantiation of the StringBuilder outside loop:
   33:  new #8; //class java/lang/StringBuilder
   36:  dup
   37:  invokespecial   #9; //Method java/lang/StringBuilder."<init>":()V
   40:  astore_2

   // [snip a few lines for initializing the loop]
   // Loading the StringBuilder inside the loop, then append:
   66:  aload_2
   67:  aload   4
   69:  invokevirtual   #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   72:  pop
   73:  aload_2
   74:  ldc #15; //String \t
   76:  invokevirtual   #14; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   79:  pop

ということは、確かに手による最適化の方がパフォーマンスが良いはずです。 for ループが短くなり、また StringBuilder を繰り返し実行します。