Splitting and Joining

Split delimited text safely, avoid the regex and trailing-empty traps in String.split, and join values back with the right separator.

SyntaxString[] parts = line.split(",", -1); // keep trailing empties String[] kv = pair.split("=", 2); // at most 2 parts String csv = String.join(",", values);

Splitting turns one String into parts: a comma-separated header, a query string, a line of a simple data file. Joining does the reverse. Both look trivial, and both have traps that produce wrong data rather than errors.

split takes a regular expression

The argument to split is a regex, not plain text. "25.0.1".split(".") returns an empty array, because . matches every character. The same goes for |, +, *, $ and ?. Escape the character ("\\.") or wrap the delimiter in Pattern.quote. The upside is that patterns like "\\s*,\\s*" split on commas and swallow the spaces around them in one step.

Empty parts

By default split removes trailing empty strings. A CSV row 42,Ada,, splits into two parts, not four, and code that reads parts[3] throws ArrayIndexOutOfBoundsException. Pass a limit of -1 to keep every field. Leading empty strings are kept, so ",a" gives an empty first part.

A positive limit caps the number of parts, and the last part keeps the rest of the text. pair.split("=", 2) is the right way to split key=value, because a value such as a base64 token may itself contain =.

Know where split stops being enough

Real CSV allows quoted fields containing commas and line breaks, and query strings are percent-encoded. Splitting gets you started; decode each part with URLDecoder, and use a proper parser for CSV from outside your system. Since Java 21, splitWithDelimiters also returns the delimiters, which helps when you need to rebuild the text.

Joining

  • String.join(", ", list) joins any Iterable of strings, or a list of arguments.
  • Collectors.joining(sep, prefix, suffix) joins the end of a stream, after mapping or filtering.
  • StringJoiner does the same when you add parts one at a time in a loop.

All three put the separator only between items, so there is no trailing comma to remove. Joining does no escaping: if a value can contain the separator, the result cannot be split back reliably.

For multi-line text, lines() (Java 11) splits on \n, \r\n or \r and returns a stream, which handles uploads from any operating system.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Parse a raw query string such as page=2&sort=name into a map inside a hand-written HTTP handler.
  • Read a comma-separated ALLOWED_ORIGINS environment variable into a list, ignoring spaces around each entry.
  • Build a Link or Allow response header by joining a list of values with ", ".

More examples

Trailing empty fields and the limit argument

Without a limit the two empty trailing fields vanish, while a leading empty field is kept. Splitting an empty String gives one empty part, not zero, so check for empty input first.

Example · java
void main() {
    String row = "42,Ada,,";
    IO.println(Arrays.toString(row.split(",")));
    IO.println(Arrays.toString(row.split(",", -1)));
    IO.println(Arrays.toString(",a,b".split(",")));
    IO.println("".split(",").length);
}
// Output:
// [42, Ada]
// [42, Ada, , ]
// [, a, b]
// 1

Delimiters that are regex characters

An unescaped dot matches every character, so every part is empty and all are dropped. Pattern.quote is the safest choice when the delimiter comes from configuration.

Example · java
void main() {
    String version = "25.0.1";
    IO.println(version.split(".").length);
    IO.println(Arrays.toString(version.split("\\.")));
    IO.println(Arrays.toString("a|b|c".split(Pattern.quote("|"))));
    IO.println(Arrays.toString("admin , editor,viewer".split("\\s*,\\s*")));
}
// Output:
// 0
// [25, 0, 1]
// [a, b, c]
// [admin, editor, viewer]

Joining lists and lines

Collectors.joining adds a prefix and suffix, which is enough for a JSON array of simple values; values that may contain quotes need escaping. lines() treats Windows and Unix line endings the same.

Example · java
void main() {
    List<String> roles = List.of("admin", "editor", "viewer");
    IO.println(String.join(", ", roles));
    IO.println(roles.stream()
            .map(r -> "\"" + r + "\"")
            .collect(Collectors.joining(",", "[", "]")));
    IO.println(String.join("/", "api", "v1", "users"));
    IO.println("one\ntwo\r\nthree".lines().toList());
}
// Output:
// admin, editor, viewer
// ["admin","editor","viewer"]
// api/v1/users
// [one, two, three]

Discussion

  • Be the first to comment on this lesson.