The best way to join Strings in Java

Joining strings in java is quite simple, just take two strings and add them using the "+" sign as follows:
String s = "One" + " Two";

Which results in: s == "One Two".

The thing is that String objects in Java are immutable, so you can't actually change any string, which means that in the following example:
String a = "One";
String x = a;
in memory now: (a == x)

a = a + " Two"; 

Will result in the following in-memory statement: (a != x) which is a reason for many many bugs in many programs.


Another side effect of immutable objects is that every "change" of the string will actually create a new one, resulting in lots of dead objects which will later on be ditched by the garabage collector, but in the meanwhile they consume memory, and will use CPU to collect them.

StringBuilder / StringBuffer

The simplest solution supplied by Java is using StringBuilder / StringBuffer (StringBuffer should be used only when you want to access the StringBuffer using multiple threads).

StringBuilder (or StringBuffer, from now on I will only talk about StringBuilder but I mean StringBuffer as well) is Mutable which by design solves all of the problems about Strings mentioned above.

The Pointer to the object won't change (solving the first problem).
The same object is used for any change of the string - solving the other 2 problems, so no expensive memory waste or of CPU on the garbage collection will be needed.


But still several problems exist with StringBuilder.
The first one isn't an actual problem but more of an annoyance.
Addition of arrays (or any collection for that matter) can only be done using iteration over the array and addition of each member of the array into the StringBuilder - tiresome.

The second problem is also not a problem but I am sure many many bugs were opened due to wrong usage because of it and is an even bigger annoyance:
Addition of an array with delimiters!
What's the problem with it? - The removal of the last delimiter.

How is it usually done?
Something like the following:
final String DELIMITER = ", "; // Please note that we wish to have an additional space after the comma
StringBuilder sb = new StringBuilder();
for (String s: strings) {
    sb.append (s).append(DELIMITER);
}

return sb.toString().substring(0, sb.toString().length - 2);

First of all look at the length of the code (and the last line should be split into two lines at least!)
Second, look at the last line, the mess there is done in order to remove the last delimiter which isn't needed after the last line - I can swear that every time I need to do it I check the javadocs to be sure how to do that substring (does it begin from 0 or 1?, should I remove 1 or two from the length in order to remove ONLY the delimiter?).

The last problem StringBuilder is just plain ridiculous.
IMHO, StringBuilder manages NULLs in a very bad way.

What will be the output of the following code?
StringBuilder sb = new StringBuilder();
sb.append("One").append(null).append(" Two");
return sb.toString();

What should one expect as output of that code?
Should it throw a NullPointerException?
Should it ignore it?

The ridiculous output result is: "Onenull Two" - It actually inserted the word null into the string ?! - why would anyone want that?

In order to solve all of the above problems and annoyances I suggest using Guava's Joiner.

Guava Joiner

Google's Guava library offers many nice utilities, one package is called ...base and has three main String manipulation classes in it, one of which is Joiner.

In order to use Joiner, you need only to declare a Joiner instance (this stage can be skipped), while chaining its two main properties which are: delimiter character/s & null handling.

It is done as follows:
Joiner joiner = Joiner.on (", ").skipNulls();

In this joiner I declared the delimiter to be a comma and a space, and chose to skip any null value, I could have used ".useForNull ("~")" instead of "skipNulls" in order to substitute any null with my chosen character/s ("~" in this example).

After declaring a joiner, all which is left is only to use it as follows:
String s = joiner.join (array);
or
String s = joiner.join (iterator)

Which will take an array (or collection) and add all of their elements to a single string delimited by the chosen delimiter without having a delimiter after the last string, while skipping any null (or substituting any null value by any value I declared above).

As simple as that - could it be any easier?


P.S. there is an other very famous library which actually does the same which is: Apache commons Lang :: StringUtils.

I have compared them thoroughly (functionality wise but didn't do any profiling) and have found them to solve all of the above problems in a satisfying way.

So I recommend using Guava or Commons-lang according to your project library dependencies (if you already have one of these libraries then just use it also for string manipulation, no need to import another library for this task).

I do find Guava to be a little bit better due to it's better null handling, commons-lang only replaces null values with empty strings, Guava has a much richer null handling mechanism as mentioned above.



Comments

Thanks, Avi. That's useful information, and well-written.

Popular posts from this blog

Profiling Java @ 2019

Shared/Pro/Managed Hosting for your site - which to choose ? (NO AFFILIATE LINKS!)

ZVR converter