Objective

Java 8 introduced a class called StringJoiner. It is a simple class which we can use to build a sequence of characters that are separated by a configured delimiter. It also supports optional prefix and suffix, which when provided would be the prefix and suffix of the concatenated string (with the delimiter). In this post we will learn about the Java 8 StringJoiner class with examples.

StringJoiner constructors

There are two constructors in the Java 8 StringJoiner class.

  • A StringJoiner with a delimiter
  • A StringJoiner with a delimiter, prefix, and suffix

StringJoiner with a delimiter

The most basic way to construct a StringJoiner instance is to just pass the delimiter (a String) using which to concatenate the multiple CharSequences that will be added later to the StringJoiner. The built string will not have any prefix or suffix.

StringJoiner with a delimiter, prefix, and suffix

We pass the delimiter, prefix and a suffix (all are Strings) to this version of the StringJoiner. The final built String will begin with the passed prefix and end with the suffix and has the individual characters/strings passed separated by the delimiter.

The Java 8 StringJoiner methods

Let us have a brief look at the methods in the Java 8 StringJoiner class. Do not worry if you don’t understand them. We will look at each of them with examples.

add

The add method takes a CharSequence and copies it as the next value of the StringJoiner. If passed a null, “null” will be added.

toString

This method is used to get the current value of the StringJoiner as a string. It returns:

prefix + {the values joined by the delimiter} + suffix

If no values are added, it returns the concatenation of prefix and suffix alone.

length

The length method returns the length of string representation of the StringJoiner (as per the toString method).

setEmptyValue

This sets a special sequence of characters to be used when no values are added to the StringJoiner i.e., when it is empty. Note that even if we add an empty string to the StringJoiner, the StringJoiner will be considered as non-empty.

merge

The merge method takes another StringJoiner and adds the contents of that StringJoiner without prefix and suffix as the next element to this StringJoiner (the StringJoiner on which the merge method is called).

Basic StringJoiner with delimiter

Let us look at how to use a basic StringJoiner which we have configured to use a delimiter only (no prefix, suffix).

Adding Strings to a StringJoiner

We build a StringJoiner instance by passing the delimiter as hyphen. Then we add three strings to it. Internally, it would mutate the StringJoiner instance each time we add a string.

StringJoiner stringJoiner = new StringJoiner("-");
stringJoiner.add("John");
stringJoiner.add("Michael");
stringJoiner.add("Kent");

System.out.println(stringJoiner.toString()); //John-Michael-Kent
System.out.println(stringJoiner.length()); //17

Calling toString returns the built string which is the individual String values passed concatenated by the delimiter.

The length method returns the length of the string that the toString method will return.

Most of the StringJoiner methods like add, merge returns the same instance of the StringJoiner (this). Hence we can chain the calls on it like,

StringJoiner stringJoiner = new StringJoiner("-")
            .add("John")
            .add("Michael")
            .add("Kent");

Adding only one element

The below example demonstrates adding only one value. In this case, the delimiter cannot be seen in the output as there is only one value in the StringJoiner.

StringJoiner stringJoiner = new StringJoiner("-");
stringJoiner.add("John");
System.out.println(stringJoiner.toString()); //John
System.out.println(stringJoiner.length()); //4

Adding no value

When nothing is added to the StringJoiner, the result of calling toString will be an empty string.

StringJoiner stringJoiner = new StringJoiner("-");
System.out.println(stringJoiner.toString()); //""

StringJoiner#setEmptyValue method

We can configure a default string to be used when the StringJoiner is ‘empty’. Thus, when no values are added to the StringJoiner, it will return this special string rather than returning an empty string.

StringJoiner  stringJoiner = new StringJoiner("-");
stringJoiner.setEmptyValue("NULL");
System.out.println(stringJoiner.toString()); //NULL

In the above code, we configured a string NULL as the empty value. Hence, the StringJoiner’s string representation will be this string when it is empty i.e., when no values are added to it.

Adding empty string and null to a StringJoiner

Adding an empty string to a StringJoiner will still be counted as something added to it. Hence, when calling the toString method, it will not return the string passed to the setEmptyValue method.

StringJoiner stringJoiner = new StringJoiner("-");
stringJoiner.setEmptyValue("NULL");
stringJoiner.add("");
System.out.println(stringJoiner.toString()); //""

If we add a null, it will return the string null back when calling toString.

StringJoiner stringJoiner = new StringJoiner("-");
stringJoiner.add(null);
System.out.println(stringJoiner.toString()); //"null"
System.out.println(stringJoiner.toString().length()); //4

StringJoiner with delimiter, prefix, and suffix

Now let us see the StringJoiner methods when using a prefix and suffix as well.

Configuring StringJoiner

StringJoiner stringJoiner = new StringJoiner("-", "{", "}")

We build a StringJoiner with

  • - as the delimiter
  • { as the prefix
  • } as the suffix

Though the example shows them as single characters, they can be any CharSequence.

StringJoiner#add

StringJoiner stringJoiner = new StringJoiner("-", "{", "}")
        .add("John")
        .add("Michael")
        .add("Kent");
System.out.println(stringJoiner.toString()); //{John-Michael-Kent}
System.out.println(stringJoiner.length()); //19

It joined the passed names using the delimiter hyphen. This is exactly what we have seen before. But when we obtained the String representation of the StringJoiner, it added the prefix ({) string to the beginning of the string and suffix (}) to the end.

The length as usual will return the length of the StringJoiner’s string representation.

Adding one element

Since there is only one value added, the delimiter will not be used. But the final string will have the prefix and suffix pre-pended and appended to it, respectively.

StringJoiner stringJoiner = new StringJoiner("-", "{", "}");
stringJoiner.add("John");
System.out.println(stringJoiner.toString()); //{John}
System.out.println(stringJoiner.length()); //6

Empty StringJoiner

If the StringJoiner is empty when calling the toString i.e., no values are added, then the result will be a concatenation of prefix + suffix alone.

StringJoiner stringJoiner = new StringJoiner("-", "{", "}");
System.out.println(stringJoiner.toString()); //{}

StringJoiner setEmptyValue

The behaviour of setEmptyValue is same as seen before. It will return this string when the StringJoiner is empty. Note that the prefix and suffix are not added to this. The passed empty value string representation is returned as it is.

StringJoiner stringJoiner = new StringJoiner("-", "{", "}");
stringJoiner.setEmptyValue("NULL");
System.out.println(stringJoiner.toString()); //NULL

Adding empty string and null to a StringJoiner

As we saw before, adding empty string will still count as some value added. Hence, the StringJoiner is not considered to be empty. Hence, the string configured as to be used for empty StringJoiner is not returned.

Note that the resultant string has prefix and suffix added.

StringJoiner stringJoiner = new StringJoiner("-", "{", "}");
stringJoiner.setEmptyValue("NULL");
stringJoiner.add("");
System.out.println(stringJoiner.toString()); //"{}"

When we add a null, the result will be prefix + null as string + suffix.

StringJoiner stringJoiner = new StringJoiner("-", "{", "}");
stringJoiner.add(null);
System.out.println(stringJoiner.toString()); //{null}

StringJoiner merge for StringJoiner with delimiter, prefix and suffix

We will build two StringJoiner instances and merge the second with the first. As stated before, this will merge the string representation of the second StringJoiner (without the prefix, suffix) as the next element in the first.

StringJoiner stringJoiner1 = new StringJoiner("-", "{", "}")
        .add("John")
        .add("Michael")
        .add("Kent");
System.out.println(stringJoiner1.toString()); //{John-Michael-Kent}

StringJoiner stringJoiner2 = new StringJoiner("-", "{", "}")
        .add("Adam")
        .add("Steve");
System.out.println(stringJoiner2.toString()); //{Adam-Steve}


//Merge 
stringJoiner1.merge(stringJoiner2);

System.out.println(stringJoiner1); //{John-Michael-Kent-Adam-Steve}

The string from the second StringJoiner (Adam-Steve) is added to the first StringJoiner. The prefix and suffix will not be added again from the second Joiner.

We can continue to add more data to the first or the second StringJoiner. Let us add a value to the first StringJoiner. This will not change the second in any way.

//adding to stringJoiner1 doesn't affect stringJoiner2
stringJoiner1.add("Zac");

//adding to stringJoiner2 doesn't affect stringJoiner1
stringJoiner2.add("Simon");

System.out.println(stringJoiner1.toString()); //{John-Michael-Kent-Adam-Steve-Zac}
System.out.println(stringJoiner2.toString()); //{Adam-Steve-Simon}

Merge with a StringJoiner with a different delimiter

Let us change the delimiter of the second StringJoiner.

StringJoiner stringJoiner1 = new StringJoiner("-", "{", "}")
        .add("John")
        .add("Michael")
        .add("Kent");
System.out.println(stringJoiner1.toString()); //{John-Michael-Kent}

StringJoiner stringJoiner2 = new StringJoiner(":", "{", "}")
        .add("Adam")
        .add("Steve");
System.out.println(stringJoiner2.toString()); //{Adam:Steve}

stringJoiner1.merge(stringJoiner2); 
System.out.println(stringJoiner1); //{John-Michael-Kent-Adam:Steve}

The merge will just take the string representation of the second StringJoiner without prefix and suffix and add it as the next value of the first StringJoiner.

We can add more values to the first as shown below. It is just that the StringJoiner now has strings with multiple delimiters.

stringJoiner1.add("Zac");
System.out.println(stringJoiner1.toString()); //{John-Michael-Kent-Adam:Steve-Zac}

Merging single element StringJoiners with same delimiters

Let us merge two StringJoiner objects which has just one element in each of them.

StringJoiner stringJoiner1 = new StringJoiner("-", "{", "}");
stringJoiner1.add("John");
System.out.println(stringJoiner1.toString()); //{John}

StringJoiner stringJoiner2 = new StringJoiner("-", "{", "}");
stringJoiner2.add("Adam");
System.out.println(stringJoiner2.toString()); //{Adam}

stringJoiner1.merge(stringJoiner2);
System.out.println(stringJoiner1); //{John-Adam}

Merging single element StringJoiners with different delimiters

We will now merge two StringJoiners configured with different delimiters, which has just one element in each of them.

StringJoiner stringJoiner1 = new StringJoiner("-", "{", "}");
stringJoiner1.add("John");
System.out.println(stringJoiner1.toString()); //{John}

StringJoiner stringJoiner2 = new StringJoiner(":", "{", "}");
stringJoiner2.add("Adam");
System.out.println(stringJoiner2.toString()); //{Adam}

stringJoiner1.merge(stringJoiner2);
System.out.println(stringJoiner1); //{John-Adam}

After merging, the final output will use the current string joiner’s delimiter only. The other StringJoiner’s delimiter is used only for building the string.

Merging single element StringJoiners with different prefix, suffix

As you already know, the prefix and suffix of the StringJoiner that we merge with doesn’t matter. It will use the current StringJoiner’s prefix, suffix in the output string.

StringJoiner stringJoiner1 = new StringJoiner("-", "{", "}")
        .add("John")
        .add("Michael")
        .add("Kent");
System.out.println(stringJoiner1.toString()); //{John-Michael-Kent}

StringJoiner stringJoiner2 = new StringJoiner("-", "[", "]")
        .add("Adam")
        .add("Steve");
System.out.println(stringJoiner2.toString()); //[Adam-Steve]

stringJoiner1.merge(stringJoiner2);
System.out.println(stringJoiner1); //{John-Michael-Kent-Adam-Steve}

Merge with an empty StringJoiner

When the other StringJoiner is empty, the merge has no effect.

StringJoiner stringJoiner1 = new StringJoiner("-", "{", "}")
        .add("John")
        .add("Michael")
        .add("Kent");
System.out.println(stringJoiner1.toString()); //{John-Michael-Kent}

StringJoiner stringJoiner2 = new StringJoiner("-", "[", "]");
System.out.println(stringJoiner2.toString()); //[]

stringJoiner1.merge(stringJoiner2);
System.out.println(stringJoiner1); //{John-Michael-Kent}

Merge with an empty StringJoiner with emptyValue set

It is an interesting case when the other StringJoiner has set an empty value string using setEmptyValue method. This does not matter and not used when merging. It is still like the case when the other StringJoiner is empty.

Remember that a StringJoiner is empty when neither add nor merge (with a non-empty StringJoiner) was called. Calling setEmptyValue does not make a StringJoiner non-empty. It is just a placeholder string to be used for the String representation of an empty StringJoiner.

StringJoiner stringJoiner1 = new StringJoiner("-", "{", "}")
        .add("John")
        .add("Michael")
        .add("Kent");
System.out.println(stringJoiner1.toString());//{John-Michael-Kent}

StringJoiner stringJoiner2 = new StringJoiner("-", "[", "]");
stringJoiner2.setEmptyValue("NO_VALUE");
System.out.println(stringJoiner2.toString()); //NO_VALUE 

stringJoiner1.merge(stringJoiner2);
System.out.println(stringJoiner1); //{John-Michael-Kent}

StringJoiner merge for StringJoiner with just delimiter

For the sake of completeness, I will provide code examples for StringJoiner merge when the StringJoiners just use a delimiter. The code is self-explanatory if you have read the above section.

StringJoiner stringJoiner1 = new StringJoiner("-")
        .add("John")
        .add("Michael")
        .add("Kent");
System.out.println(stringJoiner1.toString()); //John-Michael-Kent

StringJoiner stringJoiner2 = new StringJoiner("-")
        .add("Adam")
        .add("Steve");
System.out.println(stringJoiner2.toString()); //Adam-Steve

stringJoiner1.merge(stringJoiner2);
System.out.println(stringJoiner1); //John-Michael-Kent-Adam-Steve

Merge with a StringJoiner with a different delimiter

StringJoiner stringJoiner1 = new StringJoiner("-")
        .add("John")
        .add("Michael")
        .add("Kent");
System.out.println(stringJoiner1.toString()); //John-Michael-Kent

StringJoiner stringJoiner2 = new StringJoiner(":")
        .add("Adam")
        .add("Steve");
System.out.println(stringJoiner2.toString()); //Adam:Steve

stringJoiner1.merge(stringJoiner2);
System.out.println(stringJoiner1); //John-Michael-Kent-Adam:Steve

Merging single element StringJoiners

Irrespective of what delimiter the other StringJoiner is using, the result is the same as they have just one value in them.

//Merge with a StringJoiner using same delimiter
StringJoiner stringJoiner1 = new StringJoiner("-");
stringJoiner1.add("John");
System.out.println(stringJoiner1.toString()); //John

StringJoiner stringJoiner2 = new StringJoiner("-");
stringJoiner2.add("Adam");
System.out.println(stringJoiner2.toString()); //Adam

stringJoiner1.merge(stringJoiner2);
System.out.println(stringJoiner1); //John-Adam

//Merge with a StringJoiner using different delimiter
StringJoiner stringJoiner1 = new StringJoiner("-");
stringJoiner1.add("John");
System.out.println(stringJoiner1.toString()); //John

StringJoiner stringJoiner2 = new StringJoiner(":");
stringJoiner2.add("Adam");
System.out.println(stringJoiner2.toString()); //Adam

stringJoiner1.merge(stringJoiner2);
System.out.println(stringJoiner1); //John-Adam

Merge with an empty StringJoiner

Merging with an empty StringJoiner does not change the value of the StringJoiner. This is true even if the other StringJoiner has set the empty string value using the setEmptyValue method.

StringJoiner stringJoiner1 = new StringJoiner("-")
        .add("John")
        .add("Michael")
        .add("Kent");
System.out.println(stringJoiner1.toString()); //John-Michael-Kent

StringJoiner stringJoiner2 = new StringJoiner("-");
System.out.println(stringJoiner2.toString()); // ""

stringJoiner1.merge(stringJoiner2);
System.out.println(stringJoiner1); //John-Michael-Kent
StringJoiner stringJoiner1 = new StringJoiner("-")
        .add("John")
        .add("Michael")
        .add("Kent");
System.out.println(stringJoiner1.toString()); //John-Michael-Kent

StringJoiner stringJoiner2 = new StringJoiner("-");
stringJoiner2.setEmptyValue("NO_VALUE");
System.out.println(stringJoiner2.toString()); //NO_VALUE

stringJoiner1.merge(stringJoiner2);
System.out.println(stringJoiner1); //John-Michael-Kent

Conclusion

In this post, we learnt the Java 8 StringJoiner class and the methods in it with examples. We looked at two ways to construct a StringJoiner viz., with delimiter only, and with delimiter, prefix and a suffix. We learnt the StringJoiner methods add, merge, toString, length and setEmptyValue with examples.

Have a question? Add a comment below and I’ll be happy to answer it. Also, I recommended reading through other Java 8 posts.