Best way to convert an ArrayList to a string
Best way to convert an ArrayList to a string
Question
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.
Accepted Answer
Basically, using a loop to iterate over the ArrayList
is the only option:
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 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;
As can be seen, the compiler optimizes that loop by using a StringBuilder
, so performance shouldn't be a big concern.
(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.
Edit: 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.)
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.
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 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
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.
Popular Answer
In Java 8 or later:
String listString = String.join(", ", list);
In case the list
is not of type String, a joining collector can be used:
String listString = list.stream().map(Object::toString)
.collect(Collectors.joining(", "));
Read more… Read less…
If you happen to be doing this on Android, there is a nice utility for this called TextUtils which has a .join(String delimiter, Iterable)
method.
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...
Download the Apache Commons Lang and use the method
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.
Changing List to a readable and meaningful String is really a common question that every one may encounter.
Case 1. If you have apache's StringUtils in your class path (as from rogerdpack and Ravi Wallau):
import org.apache.commons.lang3.StringUtils;
String str = StringUtils.join(myList);
Case 2 . If you only want to use ways from JDK(7):
import java.util.Arrays;
String str = Arrays.toString(myList.toArray());
Just never build wheels by yourself, dont use loop for this one-line task.
If you were looking for a quick one-liner, as of Java 5 you can do this:
myList.toString().replaceAll("\\[|\\]", "").replaceAll(", ","\t")
Additionally, if your purpose is just to print out the contents and are less concerned about the "\t", you can simply do this:
myList.toString()
which returns a string like
[str1, str2, str3]
If you have an Array (not ArrayList) then you can accomplish the same like this:
Arrays.toString(myList).replaceAll("\\[|\\]", "").replaceAll(", ","\t")