将数组列表转换为字符串的最佳方法

我有一个ArrayList,我想把它完全输出为字符串。本质上,我想使用每个元素的toString以制表符分隔,按顺序输出它。有什么快速的方法吗?你可以循环遍历它(或删除每个元素),并将它连接到一个字符串,但我认为这将是非常缓慢的。

1386388 次浏览

循环遍历它并调用toString。没有什么神奇的方法,如果有的话,你认为它在被子下面除了循环之外会做什么?唯一的微优化是使用StringBuilder而不是String,即使这也不是一个巨大的胜利-连接字符串在幕后变成了StringBuilder,但至少如果您以这种方式编写它,您可以看到正在发生什么。

StringBuilder out = new StringBuilder();
for (Object o : list)
{
out.append(o.toString());
out.append("\t");
}
return out.toString();

这是一个O(n)算法(除非你做了一些多线程解决方案,你把列表分解成多个子列表,但我不认为这是你想要的)。

只需使用StringBuilder,如下所示:

StringBuilder sb = new StringBuilder();


for (Object obj : list) {
sb.append(obj.toString());
sb.append("\t");
}


String finalString = sb.toString();

StringBuilder将比字符串连接快得多,因为你不会在每个连接上重新实例化一个String对象。

Java 8引入了String.join(separator, list)方法;看到维塔利·费连科的回答

在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可能会产生更好的性能。)

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

正如评论中指出的,上面的编译器优化确实是在每次迭代时创建一个StringBuilder的新实例。(这一点我之前已经提到过。)

最优化的技术是使用保罗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

大多数Java项目通常都有apache-commons lang可用。StringUtils.join()方法非常好,有几种风格可以满足几乎所有需求。

public static java.lang.String join(java.util.Collection collection,
char separator)




public static String join(Iterator iterator, String separator) {
// handle null, zero and one elements before building a buffer
Object first = iterator.next();
if (!iterator.hasNext()) {
return ObjectUtils.toString(first);
}
// two or more elements
StringBuffer buf =
new StringBuffer(256); // Java default is 16, probably too small
if (first != null) {
buf.append(first);
}
while (iterator.hasNext()) {
if (separator != null) {
buf.append(separator);
}
Object obj = iterator.next();
if (obj != null) {
buf.append(obj);
}
}
return buf.toString();
}

参数:

集合 -要连接在一起的值的集合,可以为空

分隔符 -要使用的分隔符

返回: join String, null if 空迭代器输入

< p >自: 2.3 < / p >

下载Apache Commons Lang并使用该方法

 StringUtils.join(list)


StringUtils.join(list, ", ") // 2nd param is the separator.

当然,您可以自己实现它,但是它们的代码经过了充分的测试,可能是最好的实现。

我是Apache Commons库的忠实粉丝,我也认为它是对Java标准库的一个很好的补充。

如果你不想让最后一个元素后面的最后一个\t,你必须使用索引来检查,但请记住,这只在lists实现RandomAccess时“有效”(即O(n))。

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


StringBuilder sb = new StringBuilder(list.size() * apprAvg); // every apprAvg > 1 is better than none
for (int i = 0; i < list.size(); i++) {
sb.append(list.get(i));
if (i < list.size() - 1) {
sb.append("\t");
}
}
System.out.println(sb.toString());

处理尾随分隔符的一种优雅方法是使用类分离器

StringBuilder buf = new StringBuilder();
Separator sep = new Separator("\t");
for (String each: list) buf.append(sep).append(each);
String s = buf.toString();

类分隔符的toString方法在第一次调用时返回分隔符除了。因此,在打印列表时,不需要前面的分隔符(在本例中是这样)。

到目前为止,这是一个相当古老的对话,apache commons现在在内部使用StringBuilder: http://commons.apache.org/lang/api/src-html/org/apache/commons/lang/StringUtils.html#line.3045

正如我们所知,这将提高性能,但如果性能是至关重要的,那么所使用的方法可能有些低效。尽管接口是灵活的,并且允许在不同的Collection类型之间保持一致的行为,但对于list(原始问题中的Collection类型)来说有些低效。

我这样做的基础是,我们会产生一些开销,这是我们可以通过简单地遍历传统for循环中的元素来避免的。相反,有一些额外的事情在幕后发生,检查并发修改、方法调用等。另一方面,增强的for循环将导致相同的开销,因为迭代器用于Iterable对象(List)。

这是一个相当老的问题,但我想我不妨添加一个更现代的答案——使用番石榴中的Joiner类:

String joined = Joiner.on("\t").join(list);

如果你碰巧在Android上做这个,有一个很好的实用工具叫做TextUtils,它有一个.join(String delimiter, Iterable)方法。

List<String> list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
String joined = TextUtils.join(", ", list);

显然在Android之外没有太多用处,但我想把它添加到这个线程中…

如果你正在寻找一个快速的一行程序,从Java 5开始,你可以这样做:

myList.toString().replaceAll("\\[|\\]", "").replaceAll(", ","\t")

此外,如果你的目的只是打印出内容,不太关心“\t”,你可以简单地这样做:

myList.toString()

哪个返回一个字符串

[str1, str2, str3]

如果你有一个数组(不是ArrayList),那么你可以像这样完成:

 Arrays.toString(myList).replaceAll("\\[|\\]", "").replaceAll(", ","\t")

如果你正在使用Eclipse集合,你可以使用makeString()方法。

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


Assert.assertEquals(
"one\ttwo\tthree",
ArrayListAdapter.adapt(list).makeString("\t"));

如果你可以将你的ArrayList转换为FastList,你就可以摆脱适配器。

Assert.assertEquals(
"one\ttwo\tthree",
FastList.newListWith("one", "two", "three").makeString("\t"));

注意:我是Eclipse Collections的提交者。

那么这个函数呢:

public static String toString(final Collection<?> collection) {
final StringBuilder sb = new StringBuilder("{");
boolean isFirst = true;
for (final Object object : collection) {
if (!isFirst)
sb.append(',');
else
isFirst = false;
sb.append(object);
}
sb.append('}');
return sb.toString();
}

它适用于任何类型的收藏……

在Java 8或更高版本中:

String listString = String.join(", ", list);

如果list不是String类型,则可以使用连接收集器:

String listString = list.stream().map(Object::toString)
.collect(Collectors.joining(", "));

Android有一个TextUtil类,你可以使用http://developer.android.com/reference/android/text/TextUtils.html

String implode = TextUtils.join("\t", list);

下面的代码可以帮助你,

List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
String str = list.toString();
System.out.println("Step-1 : " + str);
str = str.replaceAll("[\\[\\]]", "");
System.out.println("Step-2 : " + str);

输出:

Step-1 : [1, 2, 3]
Step-2 : 1, 2, 3

要使用制表符而不是println进行分隔,可以使用打印

ArrayList<String> mylist = new ArrayList<String>();


mylist.add("C Programming");
mylist.add("Java");
mylist.add("C++");
mylist.add("Perl");
mylist.add("Python");


for (String each : mylist)
{
System.out.print(each);
System.out.print("\t");
}

也许不是最好的方式,但却是优雅的方式。

Arrays.deepToString (arrays . aslist(“测试”,“Test2”)

import java.util.Arrays;


public class Test {
public static void main(String[] args) {
System.out.println(Arrays.deepToString(Arrays.asList("Test", "Test2").toArray()));
}
}

输出

(测试,Test2)

你可以使用正则表达式。这是最简洁的了

System.out.println(yourArrayList.toString().replaceAll("\\[|\\]|[,][ ]","\t"));

在Java 8中,这很简单。参见整数列表的示例:

String result = Arrays.asList(1,2,3).stream().map(Object::toString).reduce((t, u) -> t + "\t" + u).orElse("");

或者多行版本(更容易阅读):

String result = Arrays.asList(1,2,3).stream()
.map(Object::toString)
.reduce((t, u) -> t + "\t" + u)
.orElse("");

更新-一个更短的版本

String result = Arrays.asList(1,2,3).stream()
.map(Object::toString)
.collect(Collectors.joining("\t"));

列表更改为可读且有意义的字符串确实是每个人都可能遇到的一个常见问题。

案例1。如果你有apache的StringUtils在你的类路径(从rogerdpack和Ravi Wallau):

import org.apache.commons.lang3.StringUtils;
String str = StringUtils.join(myList);

案例2。如果你只想使用JDK(7)中的方法:

import java.util.Arrays;
String str = Arrays.toString(myList.toArray());

只是永远不要自己建造轮子,不要使用循环这一行任务。

我看到相当多的例子依赖于额外的资源,但似乎这将是最简单的解决方案:(这是我在我自己的项目中使用的)这基本上只是从一个数组列表转换到一个数组,然后再转换到一个列表。

    List<Account> accounts = new ArrayList<>();


public String accountList()
{
Account[] listingArray = accounts.toArray(new Account[accounts.size()]);
String listingString = Arrays.toString(listingArray);
return listingString;
}

以下几点可以吗?

List<String> streamValues = new ArrayList<>();
Arrays.deepToString(streamValues.toArray()));

如果你碰巧在Android上,你还没有使用杰克(例如,因为它仍然缺乏支持即时运行),如果你想要更多的控制结果字符串的格式(例如,您想使用换行符分隔的元素),并使用/想使用StreamSupport库(在Java 7使用流或更早版本的编译器),你可以使用这样的(我把这个方法ListUtils类):

public static <T> String asString(List<T> list) {
return StreamSupport.stream(list)
.map(Object::toString)
.collect(Collectors.joining("\n"));
}

当然,确保在列表对象的类上实现toString()。

List<String> stringList = getMyListOfStrings();
StringJoiner sj = new StringJoiner(" ");
stringList.stream().forEach(e -> sj.add(e));
String spaceSeparated = sj.toString()

将想用作分隔符的char序列传递给new StringJoiner。如果你想做一个CSV: new StringJoiner(", ");

对于这个简单的用例,您可以简单地用逗号连接字符串。如果你使用Java 8:

String csv = String.join("\t", yourArray);

否则common -lang有一个join()方法:

String csv = org.apache.commons.lang3.StringUtils.join(yourArray, "\t");

在一行: 从[12,0,1,78,12]到12 0 1 78 12

String srt= list.toString().replaceAll("\\[|\\]|,","");