Java 8 引入了String.join(separator, list) 方法;见Vitalii Federenko's answer。
在 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 编译器会将字符串连接优化为对StringBuilder 的一系列append 操作。下面是上述程序中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 优化该循环,因此性能应该不是大问题。
(好吧,乍一看,StringBuilder 在循环的每次迭代中都会被实例化,因此它可能不是最有效的字节码。实例化并使用显式的StringBuilder 可能会产生更好的性能。)
事实上,我认为拥有任何类型的输出(无论是到磁盘还是到屏幕)都至少比担心字符串连接的性能慢一个数量级。
编辑: 正如 cmets 中所指出的,上述编译器优化确实是在每次迭代时创建 StringBuilder 的新实例。 (我之前已经注意到了。)
最优化的技术是Paul Tomblin 的响应,因为它只在for 循环之外实例化单个StringBuilder 对象。
将上述代码改写为:
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和循环的实例化) :
// 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。