# How to Use Guava's Joiner and Splitter for String Manipulation

> Master Guava's Joiner and Splitter for efficient string manipulation. Concatenate collections and parse delimited text with these powerful, thread-safe utilities.

- Repository: [Google/guava](https://github.com/google/guava)
- Tags: how-to-guide
- Published: 2026-08-10

---

**Guava's `Joiner` and `Splitter` utilities provide immutable, thread-safe mechanisms for concatenating collections into delimited strings and parsing delimited text back into lists or maps.**

Google Guava's `com.google.common.base` package contains production-ready utilities for handling delimited text without manual string concatenation or parsing loops. The `Joiner` class efficiently combines elements into a single string, while `Splitter` provides lazy, iterator-based parsing of delimited sequences. This guide demonstrates how to use Guava's Joiner and Splitter based on the implementation in the `google/guava` repository.

## Creating and Configuring Joiner Instances

The `Joiner` class uses a static factory pattern to create configured instances. Call `Joiner.on(String separator)` to specify the delimiter that will appear between elements.

Configuration methods return **new immutable instances** rather than modifying the original. This design allows safe storage of `Joiner` instances as `static final` constants.

```java
// Basic joining with semicolon separator
Joiner joiner = Joiner.on("; ");
String result = joiner.join("Harry", "Ron", "Hermione");
// Result: "Harry; Ron; Hermione"

```

### Handling Null Values

By default, `Joiner` throws a `NullPointerException` if any element is null. Control this behavior using two configuration methods defined in [`guava/src/com/google/common/base/Joiner.java`](https://github.com/google/guava/blob/main/guava/src/com/google/common/base/Joiner.java):

**`skipNulls()`** returns a new `Joiner` that silently ignores null elements during iteration.

**`useForNull(String nullText)`** returns a new `Joiner` that substitutes the provided string for any null value.

```java
List<String> names = List.of("Harry", null, "Ron");

// Skip nulls entirely
String skipped = Joiner.on("; ").skipNulls().join(names);
// → "Harry; Ron"

// Replace nulls with placeholder
String replaced = Joiner.on(",").useForNull("<null>").join(names);
// → "Harry,<null>,Ron"

```

### Appending to Existing Buffers

For high-performance scenarios, use **`appendTo(Appendable, Iterator)`** instead of `join()` to write directly to a `StringBuilder`, `Writer`, or other `Appendable` without creating intermediate strings. The core implementation in [`Joiner.java`](https://github.com/google/guava/blob/main/Joiner.java) (lines 99-112) iterates through the supplied iterator, converting each element via `toString(Object)` and inserting the separator between items.

```java
StringBuilder sb = new StringBuilder("Names: ");
Joiner.on(", ").appendTo(sb, "Alice", "Bob", "Charlie");
// sb contains: "Names: Alice, Bob, Charlie"

```

## Joining Maps with MapJoiner

Convert a `Joiner` into a **`MapJoiner`** by calling `withKeyValueSeparator(String kvSeparator)`. This inner class reuses the parent `Joiner`'s configuration for handling nulls while appending the key-value separator between each map entry. The concatenation logic resides in `MapJoiner.appendTo(Appendable, Iterator)` (lines 124-138) within [`Joiner.java`](https://github.com/google/guava/blob/main/Joiner.java).

```java
Map<String, String> params = Map.of("q", "guava", "page", "1");
String query = Joiner.on("&")
    .withKeyValueSeparator("=")
    .join(params);
// → "q=guava&page=1"

```

## Creating and Using Splitter

The **`Splitter`** class provides the inverse operation, parsing delimited text into an `Iterable<String>`. Like `Joiner`, it uses a static factory method `on(String)` or `on(CharMatcher)` to create immutable instances.

The splitter holds a **`Strategy`** interface implementation that creates a `SplittingIterator`. This iterator lazily extracts substrings by locating separator positions, avoiding unnecessary memory allocation until the iterator is advanced.

```java
String csv = "foo,bar,baz";
Iterable<String> parts = Splitter.on(',').split(csv);
// Iterates over: ["foo", "bar", "baz"]

```

### Configuring Split Behavior

Chain configuration methods to modify splitting logic. Each method returns a new `Splitter` instance preserving immutability:

- **`trimResults()`** removes leading and trailing whitespace from each substring
- **`omitEmptyStrings()`** excludes empty strings from the result
- **`limit(int maxItems)`** stops splitting after the specified number of items

The lazy evaluation occurs in `SplittingIterator.computeNext()` (lines 555-587) in [`guava/src/com/google/common/base/Splitter.java`](https://github.com/google/guava/blob/main/guava/src/com/google/common/base/Splitter.java), which calculates `separatorStart` and `separatorEnd` positions on demand.

```java
String messy = "  foo, , bar,,baz  ";
List<String> clean = Splitter.on(',')
    .trimResults()
    .omitEmptyStrings()
    .splitToList(messy);
// → ["foo", "bar", "baz"]

```

### Fixed-Length and Pattern-Based Splitting

For specialized parsing, use alternative factory methods:

**`Splitter.fixedLength(int length)`** splits the input into segments of the specified character length.

**`Splitter.on(Pattern pattern)`** or **`onPattern(String regex)`** uses regular expressions to identify separators.

```java
// Split hex string into byte pairs
String hex = "A1B2C3D4E5";
List<String> bytes = Splitter.fixedLength(2).splitToList(hex);
// → ["A1", "B2", "C3", "D4", "E5"]

// Split on whitespace using regex
Pattern whitespace = Pattern.compile("\\s+");
Iterable<String> words = Splitter.on(whitespace)
    .omitEmptyStrings()
    .split("a   b\tc\nd");
// → ["a", "b", "c", "d"]

```

## Splitting into Maps

Create a **`MapSplitter`** by calling `withKeyValueSeparator(String)` on a `Splitter` instance. This splits the input into entries, then divides each entry into key-value pairs using the specified separator.

The `MapSplitter.split(CharSequence)` method (lines 506-524) in [`guava/src/com/google/common/base/Splitter.java`](https://github.com/google/guava/blob/main/guava/src/com/google/common/base/Splitter.java) performs validation to detect duplicate keys or malformed entries (missing key-value separators).

```java
String kv = "x=1; y=2; z=3";
Map<String, String> map = Splitter.on(';')
    .trimResults()
    .withKeyValueSeparator("=")
    .split(kv);
// → {x=1, y=2, z=3}

```

## Design Principles and Performance

Both utilities follow consistent architectural patterns that make them suitable for production use:

**Immutability** – Configuration methods never mutate the original instance. This thread-safety allows storing configured instances as constants and sharing them across threads without synchronization.

**Lazy Evaluation** – `Splitter.split()` returns an `Iterable` whose `SplittingIterator` produces substrings on demand. This minimizes memory allocation when processing large inputs or when only partial results are needed.

**Appendable Integration** – `Joiner.appendTo()` enables zero-copy writing to output buffers, eliminating temporary string creation when building large text outputs.

## Summary

- Use **`Joiner.on(separator)`** to create immutable concatenation utilities that handle nulls via `skipNulls()` or `useForNull()`.
- Write directly to buffers using **`Joiner.appendTo(Appendable, Iterator)`** to avoid intermediate string allocation.
- Parse delimited text lazily with **`Splitter.on(separator)`**, which returns an iterable using `SplittingIterator` for on-demand processing.
- Chain **`trimResults()`** and **`omitEmptyStrings()`** to clean parsed data without manual post-processing.
- Convert between formats using **`MapJoiner`** (via `withKeyValueSeparator`) for query strings and **`MapSplitter`** for configuration parsing.
- Both classes reside in `guava/src/com/google/common/base/` and store configuration in immutable instances safe for static reuse.

## Frequently Asked Questions

### How do I handle null values when joining strings in Guava?

By default, `Joiner` throws a `NullPointerException` if it encounters a null element. Call **`skipNulls()`** to ignore null values entirely, or use **`useForNull(String replacement)`** to substitute a specific string for nulls. Both methods return a new `Joiner` instance, leaving the original configuration unchanged.

### What is the difference between `split()` and `splitToList()` in Guava's Splitter?

The **`split()`** method returns an `Iterable<String>` that lazily evaluates the input as you iterate, making it memory-efficient for large datasets. The **`splitToList()`** method immediately consumes the entire input and returns an immutable `List<String>`, which is convenient when you need random access or guaranteed complete parsing.

### Can I use regular expressions with Guava Splitter?

Yes. Use **`Splitter.on(Pattern pattern)`** to pass a compiled `java.util.regex.Pattern`, or use **`Splitter.onPattern(String regex)`** to provide the regular expression as a string. This allows splitting on complex separators like whitespace (`\\s+`) or multiple delimiter characters.

### Is Guava's Joiner thread-safe?

Yes. `Joiner` instances are **immutable** and thread-safe. Configuration methods such as `skipNulls()` or `useForNull()` return new instances rather than modifying the existing object, allowing you to store `Joiner` configurations as `static final` constants and share them safely across multiple threads.