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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/599161/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 16:41:18  来源:igfitidea点击:

Best way to convert an ArrayList to a string

javastringarraylist

提问by Juan Besa

I have an ArrayListthat I want to output completely as a String. Essentially I want to output it in order using the toStringof 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.

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

采纳答案by coobird

Basically, using a loop to iterate over the ArrayListis the only option:

基本上,使用循环迭代ArrayList是唯一的选择:

DO NOT use this code, continue reading to the bottom of this answer to see why it is not desirable, and which code should be used instead:

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

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);

In fact, a string concatenation is going to be just fine, as the javaccompiler will optimize the string concatenation as a series of appendoperations on a StringBuilderanyway. Here's a part of the disassembly of the bytecode from the forloop from the above program:

事实上,字符串连接会很好,因为javac编译器会将字符串连接优化为一系列append对 a的操作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;

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

可以看出,编译器通过使用 a 来优化该循环StringBuilder,因此性能应该不是一个大问题。

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

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

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.

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

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

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

The most optimized technique to use will be the response by Paul Tomblin, as it only instantiates a single StringBuilderobject outside of the forloop.

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

Rewriting to the above code to:

将上述代码改写为:

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());

Will only instantiate the StringBuilderonce outside of the loop, and only make the two calls to the appendmethod inside the loop, as evidenced in this bytecode (which shows the instantiation of StringBuilderand the loop):

只会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

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

因此,手动优化确实应该更好地执行,因为for循环内部更短,并且不需要StringBuilder在每次迭代时实例化 a 。

回答by Paul Tomblin

Loop through it and call toString. There isn't a magic way, and if there were, what do you think it would be doing under the covers other than looping through it? About the only micro-optimization would be to use StringBuilder instead of String, and even that isn't a huge win - concatenating strings turns into StringBuilder under the covers, but at least if you write it that way you can see what's going on.

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

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

回答by Mike C.

It's an O(n)algorithm either way (unless you did some multi-threaded solution where you broke the list into multiple sublists, but I don't think that is what you are asking for).

O(n)无论哪种方式,这都是一种算法(除非您做了一些多线程解决方案,将列表分成多个子列表,但我认为这不是您所要求的)。

Just use a StringBuilderas below:

只需使用StringBuilder如下:

StringBuilder sb = new StringBuilder();

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

String finalString = sb.toString();

The StringBuilderwill be a lot faster than string concatenation because you won't be re-instantiating a Stringobject on each concatenation.

StringBuilder将比字符串连接快得多,因为您不会String在每次连接时重新实例化对象。

回答by Brian

Most Java projects often have apache-commons lang available. StringUtils.join() methods is very nice and has several flavors to meet almost every need.

大多数 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();
}

Parameters:

collection- the Collection of values to join together, may be null

separator- the separator character to use

Returns: the joined String, null if null iterator input

Since: 2.3

参数:

collection- 要连接在一起的值的集合,可能为 null

分隔符- 要使用的分隔符

返回:连接的字符串,如果迭代器输入为空,则为空

自:2.3

回答by Ravi Wallau

Download the Apache Commons Lang and use the method

下载Apache Commons Lang并使用方法

 StringUtils.join(list)

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

You can implement it by yourself, of course, but their code is fully tested and is probably the best possible implementation.

当然,您可以自己实现它,但他们的代码已经过全面测试,可能是最好的实现。

I am a big fan of the Apache Commons library and I also think it's a great addition to the Java Standard Library.

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

回答by Ravi Wallau

If you don't want the last \t after the last element, you have to use the index to check, but remember that this only "works" (i.e. is O(n)) when lists implements the RandomAccess.

如果您不想要最后一个元素之后的最后一个 \t,则必须使用索引进行检查,但请记住,当列表实现 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());

回答by akuhn

An elegant way to deal with trailing separation characters is to use Class Separator

处理尾随分隔符的一种优雅方式是使用Class Separator

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

The toString method of Class Separator returns the separater, exceptfor the first call. Thus we print the list without trailing (or in this case) leading separators.

Class Separator 的 toString 方法返回分隔符,除了第一次调用。因此,我们打印没有尾随(或在这种情况下)前导分隔符的列表。

回答by Alex VI

This is quite an old conversation by now and apache commons are now using a StringBuilder internally: http://commons.apache.org/lang/api/src-html/org/apache/commons/lang/StringUtils.html#line.3045

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

This will as we know improve performance, but if performance is critical then the method used might be somewhat inefficient. Whereas the interface is flexible and will allow for consistent behaviour across different Collection types it is somewhat inefficient for Lists, which is the type of Collection in the original question.

正如我们所知,这将提高性能,但如果性能至关重要,那么所使用的方法可能会有些低效。虽然接口很灵活,并且允许跨不同集合类型的一致行为,但对于列表来说效率有点低,列表是原始问题中的集合类型。

I base this in that we are incurring some overhead which we would avoid by simply iterating through the elements in a traditional for loop. Instead there are some additional things happening behind the scenes checking for concurrent modifications, method calls etc. The enhanced for loop will on the other hand result in the same overhead since the iterator is used on the Iterable object (the List).

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

回答by Jon Skeet

This is a pretty old question, but I figure I might as well add a more modern answer - use the Joinerclass from Guava:

这是一个很老的问题,但我想我不妨添加一个更现代的答案 - 使用Guava 中Joiner类:

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

回答by JJ Geewax

If you happen to be doing this on Android, there is a nice utility for this called TextUtilswhich has a .join(String delimiter, Iterable)method.

如果您碰巧在 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);

Obviously not much use outside of Android, but figured I'd add it to this thread...

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