Everything Java

← Back to blog

Published on Wed Feb 05 2025 12:00:00 GMT+0000 (Coordinated Universal Time) by Purusothaman Ramanujam

Google Guava String Utilities: Joiner, Splitter, and CharMatcher

Introduction

Google Guava provides excellent string utilities that make text processing more efficient and readable. The three main string utilities are Joiner, Splitter, and CharMatcher, each designed for specific string manipulation tasks.

In this post, we’ll explore these utilities and learn how they can simplify your string processing code.

Adding Guava to Your Project

Maven Dependency

<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>32.1.3-jre</version>
</dependency>

Gradle Dependency

implementation 'com.google.guava:guava:32.1.3-jre'

Joiner

Joiner is a utility for joining strings with a delimiter. It’s more flexible and readable than using String.join():

import com.google.common.base.Joiner;
import java.util.Arrays;
import java.util.List;
public class JoinerExample {
public static void main(String[] args) {
List<String> fruits = Arrays.asList("apple", "banana", "cherry");
// Basic joining
String joined = Joiner.on(", ").join(fruits);
System.out.println(joined); // "apple, banana, cherry"
// Joining with null handling
List<String> mixed = Arrays.asList("apple", null, "cherry");
// Skip nulls
String joinedWithNull = Joiner.on(", ").skipNulls().join(mixed);
System.out.println(joinedWithNull); // "apple, cherry"
// Replace nulls with default value
String joinedWithDefault = Joiner.on(", ").useForNull("unknown").join(mixed);
System.out.println(joinedWithDefault); // "apple, unknown, cherry"
// Joining map entries
Map<String, Integer> map = Map.of("one", 1, "two", 2, "three", 3);
String mapJoined = Joiner.on("; ").withKeyValueSeparator("=").join(map);
System.out.println(mapJoined); // "one=1; two=2; three=3"
}
}

Joiner Features

  1. Null Handling: Skip or replace null values
  2. Key-Value Separator: Join maps with custom separators
  3. Appendable: Append to StringBuilder or other Appendable objects
  4. Immutable: Joiner instances are immutable and reusable

Splitter

Splitter is the counterpart to Joiner, providing powerful string splitting capabilities:

import com.google.common.base.Splitter;
import java.util.List;
public class SplitterExample {
public static void main(String[] args) {
String input = "apple,banana,cherry";
// Basic splitting
List<String> split = Splitter.on(",").splitToList(input);
System.out.println(split); // [apple, banana, cherry]
// Split with trimming
String messyInput = " apple , banana , cherry ";
List<String> trimmed = Splitter.on(",").trimResults().splitToList(messyInput);
System.out.println(trimmed); // [apple, banana, cherry]
// Split with empty string handling
String withEmpty = "apple,,banana,cherry";
List<String> withEmptyHandling = Splitter.on(",")
.omitEmptyStrings()
.splitToList(withEmpty);
System.out.println(withEmptyHandling); // [apple, banana, cherry]
// Split with limit
List<String> limited = Splitter.on(",").limit(2).splitToList(input);
System.out.println(limited); // [apple, banana,cherry]
// Split on multiple delimiters
String multiDelimiter = "apple,banana;cherry:orange";
List<String> multiSplit = Splitter.onPattern("[,;:]").splitToList(multiDelimiter);
System.out.println(multiSplit); // [apple, banana, cherry, orange]
}
}

Splitter Features

  1. Trimming: Automatically trim whitespace from results
  2. Empty String Handling: Omit or include empty strings
  3. Limiting: Limit the number of splits
  4. Pattern Splitting: Split on regular expressions
  5. Map Splitting: Split strings into key-value pairs

CharMatcher

CharMatcher provides utilities for working with characters and character-based operations:

import com.google.common.base.CharMatcher;
public class CharMatcherExample {
public static void main(String[] args) {
String input = "Hello123World!@#";
// Remove all digits
String noDigits = CharMatcher.digit().removeFrom(input);
System.out.println(noDigits); // "HelloWorld!@#"
// Keep only letters
String onlyLetters = CharMatcher.javaLetter().retainFrom(input);
System.out.println(onlyLetters); // "HelloWorld"
// Replace whitespace with underscores
String noWhitespace = CharMatcher.whitespace().replaceFrom("hello world", "_");
System.out.println(noWhitespace); // "hello_world"
// Count digits
int digitCount = CharMatcher.digit().countIn(input);
System.out.println(digitCount); // 3
// Check if string contains only letters
boolean onlyLettersCheck = CharMatcher.javaLetter().matchesAllOf("Hello");
System.out.println(onlyLettersCheck); // true
// Check if string contains any digits
boolean hasDigits = CharMatcher.digit().matchesAnyOf("Hello123");
System.out.println(hasDigits); // true
// Collapse whitespace
String collapsed = CharMatcher.whitespace().collapseFrom("hello world", ' ');
System.out.println(collapsed); // "hello world"
// Trim from both ends
String trimmed = CharMatcher.whitespace().trimFrom(" hello world ");
System.out.println(trimmed); // "hello world"
}
}

CharMatcher Predefined Matchers

Custom CharMatcher

public class CustomCharMatcherExample {
public static void main(String[] args) {
// Create custom matcher for vowels
CharMatcher vowels = CharMatcher.anyOf("aeiouAEIOU");
String text = "Hello World";
String consonants = vowels.removeFrom(text);
System.out.println(consonants); // "Hll Wrld"
// Combine matchers
CharMatcher lettersAndDigits = CharMatcher.javaLetterOrDigit();
CharMatcher notLettersOrDigits = lettersAndDigits.negate();
String cleaned = notLettersOrDigits.removeFrom("Hello123!@#World");
System.out.println(cleaned); // "Hello123World"
}
}

Practical Examples

CSV Processing

public class CSVProcessor {
public static List<String> parseCSVLine(String line) {
return Splitter.on(",")
.trimResults()
.omitEmptyStrings()
.splitToList(line);
}
public static String createCSVLine(List<String> values) {
return Joiner.on(",").join(values);
}
public static void main(String[] args) {
String csvLine = " apple , banana , cherry ";
List<String> parsed = parseCSVLine(csvLine);
System.out.println(parsed); // [apple, banana, cherry]
String newLine = createCSVLine(parsed);
System.out.println(newLine); // "apple,banana,cherry"
}
}

Text Cleaning

public class TextCleaner {
public static String cleanText(String text) {
return CharMatcher.whitespace()
.trimAndCollapseFrom(text, ' ')
.toLowerCase();
}
public static String removeSpecialCharacters(String text) {
return CharMatcher.javaLetterOrDigit()
.or(CharMatcher.whitespace())
.retainFrom(text);
}
public static void main(String[] args) {
String dirtyText = " Hello World!@# ";
String cleaned = cleanText(dirtyText);
System.out.println(cleaned); // "hello world"
String specialRemoved = removeSpecialCharacters(dirtyText);
System.out.println(specialRemoved); // " Hello World "
}
}

Best Practices

  1. Use Joiner for String Concatenation: More readable than StringBuilder
  2. Handle Nulls Explicitly: Always consider null handling in your string operations
  3. Use Splitter for Complex Parsing: More flexible than String.split()
  4. Leverage CharMatcher for Character Operations: More efficient than regex for simple operations
  5. Cache CharMatcher Instances: They are immutable and reusable

Common Pitfalls

  1. Forgetting Null Handling: Always consider null values in your string operations
  2. Overusing Regex: Use CharMatcher for simple character operations instead of regex
  3. Not Trimming Results: Use trimResults() when splitting user input
  4. Ignoring Empty Strings: Consider whether to omit empty strings in splits

Performance Considerations

  1. CharMatcher is Fast: Much faster than regex for simple character operations
  2. Immutable Objects: Joiner and Splitter instances can be reused
  3. Lazy Evaluation: Splitter uses lazy evaluation for large strings
  4. Memory Efficient: CharMatcher operations are memory efficient

Conclusion

Guava’s string utilities provide powerful, readable, and efficient ways to handle string operations. Joiner simplifies string concatenation, Splitter offers flexible string parsing, and CharMatcher provides fast character-based operations.

These utilities can significantly improve your code’s readability and performance compared to manual string manipulation or regex operations. Start with the basic operations and gradually explore more advanced features as your needs grow.

Resources

Written by Purusothaman Ramanujam

← Back to blog