Bits of Knowledge: Joining Strings in Java
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.
Concatenating Strings
Concatenate Operator
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 String
.
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 String
s are safe to use and popular in various APIs. It's just the concatenation is really slow, especially on large texts.
String Builder Class
When there is a need to join a large number of strings, the StringBuilder
class is the best tool to use in this problem. This is done by using its .append()
method.
Instances of StringBuilder
is usually used to build strings that has usually unknown size and assumed to be very large.
What made 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.
String .join()
Method
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.
The StringJoiner
class is very useful when building multiple strings from a single source.
Conclusion
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.
Starting from Java 8, String.join()
method exists in the standard API to create delimited strings from a collection or arrays. This approach is favored when doing simple joins with delimiter.
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.
Internally, String.join()
and Collectors.joining()
is using the StringJoiner
class. The StringJoiner
is not really used by itself but behind the scenes by using String.join()
or 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.