StringJoiner in Java – Joining Strings with Delimiters
java.util.StringJoiner, added in Java 8, builds a string from a sequence of values separated by a delimiter, optionally wrapped in a prefix and suffix. It removes the classic "trailing comma" problem of manual concatenation and powers String.join and Collectors.joining behind the scenes.
This tutorial covers its constructors and methods, empty values, merging joiners, and the related joining APIs you will use most often.
1. Constructors and Methods
| Constructor / method | Description |
|---|---|
StringJoiner(CharSequence delimiter) | Joiner with a delimiter only |
StringJoiner(delimiter, prefix, suffix) | Also wraps the result |
add(CharSequence) | Appends a value; returns the joiner for chaining |
setEmptyValue(CharSequence) | Text returned when nothing has been added |
merge(StringJoiner other) | Adds another joiner's content as one element |
length() | Length of the current result |
toString() | The joined string |
2. Joining with Delimiter, Prefix and Suffix
import java.util.StringJoiner;
public class JoinerBasics {
public static void main(String[] args) {
// Delimiter only
StringJoiner langs = new StringJoiner(", ");
langs.add("Java").add("Kotlin").add("Scala"); // add() returns the joiner
System.out.println(langs);
// Delimiter, prefix and suffix
StringJoiner json = new StringJoiner(", ", "[", "]");
for (String id : new String[] {"\"A-17\"", "\"B-42\"", "\"C-99\""}) json.add(id);
System.out.println(json + " (length " + json.length() + ")");
// Empty joiner: prefix + suffix by default, or a custom empty value
StringJoiner none = new StringJoiner(", ", "{", "}");
System.out.println("empty default: " + none);
none.setEmptyValue("no items");
System.out.println("empty custom: " + none);
none.add("pen");
System.out.println("after add: " + none);
}
}
Java, Kotlin, Scala
["A-17", "B-42", "C-99"] (length 24)
empty default: {}
empty custom: no items
after add: {pen}
- The delimiter appears only between elements, so there is no trailing separator to remove.
- An empty joiner returns
prefix + suffixunlesssetEmptyValuewas called. - Adding
nullappends the text "null".
3. Merging Joiners
import java.util.StringJoiner;
public class MergeJoiners {
public static void main(String[] args) {
StringJoiner backend = new StringJoiner(", ", "Backend[", "]");
backend.add("Java").add("Go");
StringJoiner frontend = new StringJoiner(" | ", "Frontend(", ")");
frontend.add("React").add("Angular");
// merge() adds the other joiner's CONTENT (without its prefix/suffix) as one element
backend.merge(frontend);
System.out.println(backend);
}
}
Backend[Java, Go, React | Angular]
merge takes the other joiner's content without its prefix and suffix and adds it as a single element using this joiner's delimiter. The other joiner is unchanged.
4. String.join and Collectors.joining
In everyday code you often use the higher-level APIs built on StringJoiner:
import java.util.List;
import java.util.stream.Collectors;
public class JoinAlternatives {
record City(String name, int population) { }
public static void main(String[] args) {
List<String> tags = List.of("java", "spring", "kafka");
System.out.println(String.join(" #", tags)); // String.join (uses StringJoiner)
List<City> cities = List.of(new City("Mumbai", 20), new City("Pune", 7), new City("Kochi", 2));
String big = cities.stream()
.filter(c -> c.population() > 5)
.map(City::name)
.collect(Collectors.joining(", ", "Big cities: ", ".")); // Collectors.joining
System.out.println(big);
String sql = "SELECT * FROM users WHERE id IN "
+ List.of(3, 8, 15).stream().map(String::valueOf).collect(Collectors.joining(", ", "(", ")"));
System.out.println(sql);
}
}
java #spring #kafka Big cities: Mumbai, Pune. SELECT * FROM users WHERE id IN (3, 8, 15)
| Tool | Best for |
|---|---|
String.join(delim, elements) | Joining an array or Iterable of strings in one call |
Collectors.joining(delim, prefix, suffix) | Joining at the end of a stream pipeline |
StringJoiner | Adding values gradually, conditionally, or when you need setEmptyValue/merge |
StringBuilder | General string building without a regular delimiter |
PreparedStatement parameters to avoid SQL injection.5. Summary
StringJoinerjoins values with a delimiter and optional prefix and suffix.setEmptyValuecontrols the result when nothing is added;mergecombines joiners.String.joinandCollectors.joiningare convenient front-ends built on it.- It avoids manual trailing-delimiter handling, making code shorter and clearer.
Related Tutorials
Frequently Asked Questions (FAQ)
Q1. What is StringJoiner in Java?
A Java 8 class in java.util that joins strings with a delimiter and optional prefix and suffix.
Q2. What is the difference between StringJoiner and StringBuilder?
StringJoiner is specialised for delimited lists and handles separators, prefix and suffix automatically. StringBuilder is a general-purpose mutable string.
Q3. What does setEmptyValue() do?
It sets the string returned by toString() when no elements have been added, instead of just prefix and suffix.
Q4. What does merge() do?
It appends another joiner's contents, without its prefix and suffix, as one element of this joiner.
Q5. Is StringJoiner thread-safe?
No. Use it from one thread, or synchronize access externally.
Q6. What happens if I add null?
The text "null" is added, just as with string concatenation.
Q7. Can the delimiter be null?
No. A null delimiter, prefix or suffix throws NullPointerException.
Q8. How is String.join related to StringJoiner?
String.join is a convenience method that joins elements with a delimiter, using the same logic internally.
Q9. How do I join a list in a stream?
Use Collectors.joining(", "), optionally with prefix and suffix arguments.
Q10. Can StringJoiner join numbers?
It accepts CharSequence, so convert numbers first with String.valueOf, or use a stream with map(String::valueOf).
Q11. What does length() return for an empty joiner?
The length of the empty value: prefix plus suffix, or the custom empty value if set.
Q12. Which Java version introduced StringJoiner?
Java 8, along with String.join and the Stream API.
Q13. How do I avoid a trailing comma when joining?
Use StringJoiner, String.join or Collectors.joining; they put the delimiter only between elements.
Q14. Can I remove an element from a StringJoiner?
No. It only supports adding. Build the list of values first if you need to filter or remove.
Q15. Is StringJoiner final?
Yes. It cannot be extended.
Q16. Can I reuse a StringJoiner?
There is no reset method; create a new joiner for each result.
Q17. Does StringJoiner work with Iterable?
Not directly; loop and call add, or use String.join(delimiter, iterable).
Q18. How do I format a list as [a, b, c]?
new StringJoiner(", ", "[", "]"), or list.toString() for standard collections.
Q19. Is StringJoiner faster than StringBuilder?
Performance is similar. Choose StringJoiner for clarity when joining delimited values.
Q20. What is the opposite of joining?
Splitting, done with String.split, Pattern.splitAsStream or the legacy StringTokenizer.