将 ArrayList 转换为字符串的最佳方法

将 ArrayList 转换为字符串的最佳方法

问题描述:

我有一个 ArrayList,我想将其完全输出为字符串.本质上,我想使用由制表符分隔的每个元素的 toString 按顺序输出它.有没有什么快速的方法可以做到这一点?您可以遍历它(或删除每个元素)并将其连接到一个字符串,但我认为这会很慢.

I have an ArrayList that I want to output completely as a String. Essentially I want to output it in order using the toString of each element separated by tabs. Is there any fast way to do this? You could loop through it (or remove each element) and concatenate it to a String but I think this will be very slow.

Java 8 引入了一个 String.join(separator, list) 方法;参见 Vitalii Federenko 的回答.

Java 8 introduces a String.join(separator, list) method; see Vitalii Federenko's answer.

在 Java 8 之前,使用循环遍历 ArrayList 是唯一的选择:

Before Java 8, using a loop to iterate over the ArrayList was the only option:

请勿使用此代码,继续阅读此答案的底部以了解为什么不希望使用该代码,以及应改用哪些代码:

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 循环中反汇编字节码的一部分:

In fact, a string concatenation is going to be just fine, as the javac compiler will optimize the string concatenation as a series of append operations on a StringBuilder anyway. Here's a part of the disassembly of the bytecode from the for loop from the above program:

   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 优化了该循环,因此性能不应该是一个大问题.

As can be seen, the compiler optimizes that loop by using a StringBuilder, so performance shouldn't be a big concern.

(好吧,乍一看,StringBuilder 正在循环的每次迭代中被实例化,因此它可能不是最有效的字节码.实例化和使用显式 StringBuilder 可能会产生更好的性能.)

(OK, on second glance, the StringBuilder is being instantiated on each iteration of the loop, so it may not be the most efficient bytecode. Instantiating and using an explicit StringBuilder would probably yield better performance.)

事实上,我认为拥有任何类型的输出(无论是到磁盘还是到屏幕)至少比不必担心字符串连接的性能慢一个数量级.

In fact, I think that having any sort of output (be it to disk or to the screen) will be at least an order of a magnitude slower than having to worry about the performance of string concatenations.

正如评论中所指出的,上述编译器优化确实在每次迭代时创建了一个 StringBuilder 的新实例.(我之前已经注意到了.)

As pointed out in the comments, the above compiler optimization is indeed creating a new instance of StringBuilder on each iteration. (Which I have noted previously.)

要使用的最优化技术将是 Paul Tomblin,因为它只在 for 循环之外实例化单个 StringBuilder 对象.

The most optimized technique to use will be the response by Paul Tomblin, as it only instantiates a single StringBuilder object outside of the for loop.

将上述代码改写为:

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 一次,并且只会在循环内对 append 方法进行两次调用,如此字节码所示(显示StringBuilder 和循环的实例化):

Will only instantiate the StringBuilder once outside of the loop, and only make the two calls to the append method inside the loop, as evidenced in this bytecode (which shows the instantiation of StringBuilder and the loop):

   // 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.

So, indeed the hand optimization should be better performing, as the inside of the for loop is shorter and there is no need to instantiate a StringBuilder on each iteration.