Backend Development 8 min read

Understanding Java's StringJoiner: Implementation, Usage, and Design Insights

This article examines Java's StringJoiner class, explaining why it was introduced, how it builds on StringBuilder, detailing its constructors, methods such as add, merge, length, and toString, and provides code examples and analysis of its design choices and practical usage.

Top Architect
Top Architect
Top Architect
Understanding Java's StringJoiner: Implementation, Usage, and Design Insights

When reading project code the author discovered the StringJoiner class and found it interesting because it wraps StringBuilder to simplify concatenating strings with delimiters.

Using a plain StringBuilder to build a comma‑separated list requires manual handling of the delimiter:

StringBuilder sb = new StringBuilder();
IntStream.range(1, 10).forEach(i -> {
    sb.append(i + "");
    if (i < 10) {
        sb.append(",");
    }
});

With StringJoiner the same task becomes concise:

StringJoiner sj = new StringJoiner(",");
IntStream.range(1, 10).forEach(i -> sj.add(i + ""));

The class also offers less‑known features such as setEmptyValue , merge , and length .

Member variables (excerpt from JDK source) :

private final String prefix;
private final String delimiter;
private final String suffix;
private StringBuilder value;
private String emptyValue;

The constructor validates arguments, copies them defensively, and pre‑computes emptyValue as prefix + suffix :

public StringJoiner(CharSequence delimiter,
                    CharSequence prefix,
                    CharSequence suffix) {
    Objects.requireNonNull(prefix, "The prefix must not be null");
    Objects.requireNonNull(delimiter, "The delimiter must not be null");
    Objects.requireNonNull(suffix, "The suffix must not be null");
    this.prefix = prefix.toString();
    this.delimiter = delimiter.toString();
    this.suffix = suffix.toString();
    this.emptyValue = this.prefix + this.suffix;
}

The add method delegates to prepareBuilder , which lazily creates the internal StringBuilder , adds the prefix on first use, and inserts the delimiter before subsequent elements:

public StringJoiner add(CharSequence newElement) {
    prepareBuilder().append(newElement);
    return this;
}
private StringBuilder prepareBuilder() {
    if (value != null) {
        value.append(delimiter);
    } else {
        value = new StringBuilder().append(prefix);
    }
    return value;
}

The toString implementation returns emptyValue when no elements have been added; otherwise it appends the suffix only when needed, preserving the original builder for further operations:

public String toString() {
    if (value == null) {
        return emptyValue;
    } else {
        if (suffix.equals("")) {
            return value.toString();
        } else {
            int initialLength = value.length();
            String result = value.append(suffix).toString();
            value.setLength(initialLength);
            return result;
        }
    }
}

The merge method combines another StringJoiner without duplicating delimiters, carefully handling the case where the other joiner is the same instance:

public StringJoiner merge(StringJoiner other) {
    Objects.requireNonNull(other);
    if (other.value != null) {
        final int length = other.value.length();
        StringBuilder builder = prepareBuilder();
        builder.append(other.value, other.prefix.length(), length);
    }
    return this;
}

The length method reports the total length including prefix, delimiter, added elements, and suffix (or the pre‑computed emptyValue when empty):

public int length() {
    return (value != null ? value.length() + suffix.length() : emptyValue.length());
}

Finally, setEmptyValue lets users customize the representation of an empty joiner:

public StringJoiner setEmptyValue(CharSequence emptyValue) {
    this.emptyValue = Objects.requireNonNull(emptyValue, "The empty value must not be null").toString();
    return this;
}

In summary, StringJoiner builds on StringBuilder to handle prefixes and delimiters automatically, defers suffix handling to toString and length , and provides convenient merging and empty‑value customization, making it a useful utility in backend Java development.

BackendJavaJDKStringBuilderCodeAnalysisStringJoiner
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.