One of the most common programmatic tasks in Java is to join String array to a single String. It might be useful for logging an array content, or to build an SQL query, or for many more reasons. The point is - there is no single convenient solution supported by the language, but there are many options to concatenate multiple Strings into one. In this post, I will show you a few such options and I will execute simple performance tests to compare their efficiencies.
Join manually with StringBuilder
That is probably the first choice for everybody that does not know any specific method. We can iterate over the array elements and append them to the StringBuilder instance.
private String stringBuilder(String[] array) {
StringBuilder builder = new StringBuilder();
for (String s: array) {
builder.append(s).append(",");
}
builder.deleteCharAt(builder.length() - 1);
return builder.toString();
}
Of course, the separator must be added after each item except the last one. In this implementation I avoid checking an IF statement in each iteration. I just remove the last character at the end.
String.join method
Those that do not want to waste their time on writing so much code, there is a dedicated static method in String class - join
. It is available since Java 1.8, so it has been there for a long time.
private String stringJoin(String[] array) {
return String.join(",", array);
}
The implementation is very simple. The String.join
method needs two arguments: a separator and an array.
StringJoiner
Java 1.8 made us happy in many different ways. For the first time appeared StringJoiner class. Yes, a dedicated class to just join Strings from an array.
private String stringJoiner(String[] array) {
StringJoiner joiner = new StringJoiner(",");
for (String s : array) {
joiner.add(s);
}
return joiner.toString();
}
That is not so convenient to use it with an array of Strings, because we still need to iterate over the elements ourselves, but it is still a valid option. You may also try using StringJoiner with a Java Stream to avoid an explicit for loop.
Stream Collector
As we speak about Java Streams in Java 1.8, Strings can be joined using a Stream of elements collected by joining them.
private String streamCollector(String[] array) {
return Arrays.stream(array).collect(Collectors.joining(","));
}
The array must be converted to a Stream first, but the rest is easy. The elements can be collected using a Joining Collector.
By the way, do you know how to convert a list to a map with Java Streams?
Guava Joiner
Do you think that it is enough of native Java methods to join String array? Maybe. There are also 3rd party libraries that can help with that. One of them is Guava:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
It provide a special Joiner class.
private String guavaJoiner(String[] array) {
return Joiner.on(",").join(array);
}
It is initialized by calling the static on method with a separator, then the join method allows providing an array and does the joining.
Apache Commons
Another worth mention 3rd party library is Apache Commons.
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
It provide a utility class for a bunch of String operations like joining array elements to a String with a separator.
private String apacheCommons(String[] array) {
return StringUtils.join(array, ",");
}
The static join method gets an array and a separator and does the rest.
Performance test set up
As you already know the possible solutions, let's see how they will be tested.
I prepared a test class with an array of String elements. There is a test method called testPerformance()
. It measures a time duration and prints it to the console.
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PerformanceTest {
private static final int NUM_OF_ELEMENTS = 10000;
private String[] array;
@BeforeEach
void setUp() {
array = new String[NUM_OF_ELEMENTS];
for (int i = 0; i < NUM_OF_ELEMENTS; i++) {
array[i] = String.valueOf(i);
}
}
@Test
void testPerformance() {
long startTime = System.currentTimeMillis();
// execute the method here
String result = stringBuilder(array);
// String result = stringJoin(array);
// String result = stringJoiner(array);
// String result = streamCollector(array);
// String result = guavaJoiner(array);
// String result = apacheCommons(array);
System.out.println("Took " + (System.currentTimeMillis() - startTime) + " ms.");
System.out.println(result);
}
}
The testPerformance
method contains execution of all the methods described above. Currently, only one is uncommented and will be executed. Subsequently, I will repeat the test for each method separately focusing on the time duration printed to the console. To make it quasi-professional, I will run each method 3 times, exclude the extreme values (the lowest and the greatest) and I will use the middle one as a result.
Performance test result
The table below contains these middle durations for each method.
Method name | Duration [ms] |
stringBuilder | 2 |
stringJoin | 10 |
stringJoiner | 9 |
streamCollector | 9 |
guavaJoiner | 11 |
apacheCommons | 19 |
Surprisingly, at least for me, the fastest method was using a StringBuilder with quite a big difference. Apache Commons appeared to be twice slow than average.
Mind that the test scenario contains joining only once a long array of Strings. The results might be different if the array were much shorter. Feel free to perform such a test if you want. This post contains all the necessary code.
Additionally, speed is not always the only factor. Memory usage is also important in many cases. This test did not measure that. You should craft a test to your specific needs if you need to find the best method in your case, not a good-enough one.