In Java, there are different strategies to concatenate strings together. In this article, we will explain those strategies and how they work.
String Concatenation Operator
The string concatenation operator (+) is the most common way to concatenate strings:
String message = "He" + "ll" + "o!";
The way string concatenation operation works internally highly depends on the Java compiler and the Java version you’re using. For instance, before Java 9, the previous expression might be directly optimized by the compiler to something like this:
String message = new StringBuilder("He").append("ll").append("o!").toString();
The reason is that, unlike String
, the StringBuilder
is mutable. Hence, it’s more efficient in terms of performance and memory.
After Java 9, the optimization is not that straightforward. Instead, the Java compiler uses different strategies on runtime to highly optimize this operation. For more details, you can check this question (How is String concatenation implemented in Java 9?) on StackOverflow.
String concat() Method
The concat()
method appends a string to the end of another and returns the result:
String firstName = "Amr";
String secondName = "Saeed";
String fullName = firstName.concat(" ").concat(secondName);
Unlike the string concatenation operator, which is dynamic and highly dependent on the compiler and Java version, the concat()
method falls under the String
class and has a fixed implementation.
One interesting difference between the concat()
method and string concatenation operator is the way they handle null
values. If in the previous code the secondName
was null
, the concat()
method would raise NullPointerException
. But for the string concatenation operator, the fullName
would hold Amr null
due to converting the null
value into a string.
StringBuilder Class
The StringBuilder
class is a class that implements the CharSequence
interface in Java. It is the same interface the String
class implements.
As mentioned earlier, the StringBuilder
is the mutable version of the string, which means that it allows modifying the content without creating new objects. That’s why the StringBuilder
is more efficient when it comes to string operations.
String message = new StringBuilder("He").append("ll").append("o!").toString();
One thing you need to be aware of is that StringBuilder
methods are not synchronized, hence, not thread-safe. This means that it’s not safe for multiple threads to manipulate it simultaneously.
Regarding null
handling, if a variable with null
values is passed to the StringBuilder
constructor, it will throw a NullPointerException
. If it’s passed to the append()
method, it will append the null
value as a string.
StringBuffer Class
Like the StringBuilder
class, the StringBuffer
implements the CharSequence
interface in Java and behaves the same way.
The only difference between the StringBuilder
and StringBuffer
is that the StringBuffer
methods are synchronized, hence, thread-safe. This means that it is suitable for multi-threaded environments and guarantees data integrity.
String message = new StringBuffer("He").append("ll").append("o!").toString();
The general rule of thumb is to use the StringBuilder
as it is more efficient than the StringBuffer
because of the synchronization overhead. So, unless you need the thread-safety feature of the StringBuffer
, just use the StringBuilder
.
String format() Method
The format()
method is a static method under the String
class. It’s commonly used when you want to inject arguments in a sentence based on their types without splitting it in the code to achieve readability. It’s similar to the printf
method in C/C++ if you’re familiar with.
String firstName = "Amr";
String lastName = "Saeed";
String message = String.format("My name is %s %s.", firstName, lastName);
If the string to be formatted holds a null
value, the format method will raise a NullPointerException
. If one of the arguments to be injected is null
, the method will treat the null
value as a string and inject it normally into the sentence.
One interesting feature of the format()
method is that it accepts an optional Locale
parameter. This parameter helps applying some rules to the formatted string based on the region.
double number = 25.32;
String message = "Number is %.2f";
String usFormatted = String.format(Locale.US, message, number);
String germanyFormatted = String.format(Locale.GERMANY, message, number);
The usFormatted
variable holds the value Number is 25.32
while the germanyFormatted
variable holds the value Number is 25,32
.
StringJoiner Class
The StringJoiner
class has been introduced in Java 8. It’s very useful in scenarios where you want to join a number of strings with a delimiter and optionally a prefix and suffix.
StringJoiner stringJoiner = new StringJoiner(",", "[", "]");
String numbers = stringJoiner.add("One").add("Two").add("Three").toString();
The numbers
variable holds the value [One,Two,Three]
.
If one of the values to be joined is null
, the null
will be joined as a string.
String join() Method
The join
static method of the String
class was also introduced in Java 8, it works as a shortcut to join a number of strings with a delimiter. It takes the delimiter and the strings to be joined as parameters.
String numbers = String.join(",", "One", "Two", "Three");
The numbers
variable holds the value One,Two,Three
.
If one of the values to be joined is null
, the null
will be joined as a string.
Collectors joining() Method
Probably you heard about streams in Java 8. The joining()
static method of the Collectors
class in the streams package can be used also to join elements of an array with a delimiter and optionally a prefix and suffix.
List<String> numberList = Arrays.asList("One", "Two", "Three");
String numbers = numberList.stream().collect(Collectors.joining(",", "[", "]"));
The numbers
variable holds the value [One,Two,Three]
.
Like the rest of the join strategies, if the list contains a null
value, it will be joined as a string.