Joining strings is one of the most used operation in any software development, not just in Java.
Collecting texts, generating reference numbers, completing a name or address, or those simple log message formatting – they all do string joining.
Let's see different ways to do it.
In Java, the string concatenate operator is the
+ symbol. This is one of the most used method and one we always see in various Java tutorials.
The concept is very simple: Join the string at the left side of the
+ symbol and the value at the right side. Just make sure that the value on the left side of
+ symbol is a type of
The drawback is that this operation is relatively expensive. Internally, joining two strings using the concatenate operator involves 3 strings. These are the 2 strings to be joined and the 3rd string created from the combined size of the first 2 strings – the 3rd string will contain the result of the concatenate operation. This is due to the way
String stores its value internally: using a
byte and instances of
String are immutable by contract, that is why
Strings are safe to use and popular in various APIs. It's just the concatenation is really slow, especially on large texts.
String Builder Class
StringBuilder is usually used to build strings that has usually unknown size and assumed to be very large.
StringBuilder better than the
+ symbol when joining string is the way it stores its characters. Unlike
String's usages of a byte array,
StringBuilder works more like an
ArrayList – the storage grows or shrinks depending on the content stored.
Remember to use
StringBuilder when concatenating in a loop or any unknown-sized operations.
Joining Strings with Delimiter
Sometimes, we need to join strings with delimiters between the elements. Doing it using the methods above,
+ symbol and the
StringBuilder class, you will require to implement additional logic on the beginning of the collection to properly delimit the strings:
Notice the tailing comma after the word
lychee from the example above. This is not the intended result. The delimiter should not appear after the last element of the collection. This can be fixed by adding a special handling to either the beginning or the end of the string:
In this example, the tailing delimiter is now gone. However, the algorithm used seems unorthodox – we are used to do loops starting with element 0 and accumulating to an empty container. In this example, the loop starts at 1 and the accumulator has an initial value given to it.
Also notice that this method will break if the collection to join is empty. This problem can be easily fixed by adding a check before starting the join operation. As you can see, the algorithm is starting to go complex.
Starting from Java 8, a new static method
.join() is provided to easily create joined strings with delimiter:
As you can see, the code is much simpler. There are no loops needed to implement. Just give the delimiter and the collection to join.
Collecting from Streams
It is possible to join strings that are extracted by mapping an object. There is a built-in collector in the Streams API that joins the strings with a given delimiter:
This method is a little more complex, which is just fair since the data structure we are getting the string from is also more complex.
String Joiner Class
There is a specialized class that acts just like a
StringBuilder, but is specifically made for joining strings with delimiter: the
StringJoiner class is very useful when building multiple strings from a single source.
There are multiple ways to join strings. These multiple approaches to join strings exists to handle different cases.
The simplest way to join strings is to use the
+ operator in strings. This is used in the simplest use cases of join – such as creation of log messages or exception message.
Another popular way to build strings is by using the
StringBuilder class. This approach is usually done when joining strings from a collection of unknown size. This approach is favored due to its performance.
When working with streams, the
Collectors.joining() is used in the
.collect() terminal method to join strings. This approach is favored when joining strings from objects.
Collectors.joining() is using the
StringJoiner class. The
StringJoiner is not really used by itself but behind the scenes by using
Collectors.joining() via their respective APIs. Using
StringJoiner by itself means there is a special case that strings and streams cannot handle. Personally, I used it to create multiple strings from a single collection.