skip to content

How does String.split() work, and why is its delimiter argument a regular expression with surprising trailing-empty-string behavior?

level: middleimportance: must knowfreq 74%

answer

  1. delimiter is a REGEX — escape . | \ etc.
  2. default limit 0 → drops TRAILING empty strings
  3. negative limit → keeps trailing empties
  4. positive limit N → max N elements, last keeps the rest
  5. leading empty always kept; Pattern.quote for literal

basics

~10 s

split breaks a string into an array of pieces around a delimiter. The delimiter is a regex, not plain text, and by default empty pieces at the end of the result are dropped.

solid answer

~40 s

String.split(regex) splits around matches of a regular expression and returns a String[]. Because the argument is a regex, special characters like '.', '|', '+', '(' must be escaped (e.g. split("\\.") to split on a literal dot). The single-arg form applies a default limit of 0, which means: apply the pattern as many times as possible AND remove trailing empty strings. The two-arg split(regex, limit) changes that: a positive limit caps the array size and keeps the last chunk unsplit; a negative limit applies the pattern fully but keeps trailing empties; limit 0 is the default. A leading empty string (when the input starts with the delimiter) is always kept. For a single literal character, Java has an optimized fast path. If you need literal splitting, use Pattern.quote() or call split with an escaped pattern.

code

java · 5 lines
java
"a,,b,,".split(",");      // ["a", "", "b"]      (trailing empties dropped)
"a,,b,,".split(",", -1);  // ["a", "", "b", "", ""]  (kept)
"a,b,c,d".split(",", 2);  // ["a", "b,c,d"]       (last keeps remainder)
"1.2.3".split(".");       // []  -> '.' is regex 'any char'
"1.2.3".split("\\.");     // ["1", "2", "3"]       (literal dot)

go deeper

for a junior

Knows split returns an array of pieces around a delimiter and that you must escape a dot to split on it literally.

for a middle

Understands the argument is a regex, and that the default drops trailing empty strings; can pick the right escape.

for a senior

Explains all three limit behaviors precisely, the leading-vs-trailing empty asymmetry, Pattern.quote, and the single-char fast path.

for a principal

Sets parsing conventions (precompiled Pattern reuse, negative-limit for field fidelity, avoiding regex for fixed delimiters) and reviews for these gotchas across a codebase.

## What split does `split` divides a string into an array of substrings (`String[]`) by cutting it wherever a **delimiter** occurs, and removing the delimiter itself. `"a,b,c".split(",")` → `["a", "b", "c"]`. ## The delimiter is a REGEX, not literal text The single most important gotcha: the argument is a **regular expression** (a regex — a pattern language where certain characters have special meaning), not a literal string. So: - `"."` in regex means 'any single character', so `"a.b.c".split(".")` returns an **empty array**, not `["a","b","c"]`, because every character matches and all pieces are empty (and trailing empties are stripped). - To split on a **literal** dot you must escape it: `split("\\.")` (in Java source `\\.` is the two-character regex `\.`, meaning 'a literal dot'). Other regex metacharacters needing escaping include `| ? * + ( ) [ ] { } ^ $ \`. To split on an arbitrary literal string safely, wrap it: `split(Pattern.quote(delim))`. ## The default limit and trailing empty strings `split(regex)` is shorthand for `split(regex, 0)`. The `limit` parameter controls two things — array size and trailing-empty handling: - **limit == 0 (default):** the pattern is applied as many times as possible, and **trailing empty strings are discarded**. `"a,,b,,".split(",")` → `["a", "", "b"]` — the two trailing empties vanish. This surprises people who expect to count fields. - **limit > 0 (positive):** the result has **at most** `limit` elements; the pattern is applied at most `limit - 1` times, and the **last element holds the entire remaining unsplit text** (including any delimiters). Trailing empties are **kept** within that cap. `"a,b,c,d".split(",", 2)` → `["a", "b,c,d"]`. - **limit < 0 (negative):** the pattern is applied as many times as possible and trailing empty strings are **kept**. `"a,,b,,".split(",", -1)` → `["a", "", "b", "", ""]`. So if you must preserve every field (e.g. parsing CSV-ish data where trailing columns may be empty), use a **negative limit**. ## Leading empties are different An empty string at the **start** (input begins with the delimiter) is always preserved: `",a,b".split(",")` → `["", "a", "b"]`. Only **trailing** empties are subject to the limit-0 stripping. ## Empty-pattern and no-match cases If the regex never matches, the result is a one-element array containing the original string. Splitting on the empty pattern `""` splits between every character. ## Performance Single-arg `split` compiles the regex each call. For a delimiter that is a **single literal char** (no metacharacters), the JDK uses an optimized fast path that avoids the full regex engine. For repeated splitting with the same multi-char pattern, precompile once with `Pattern.compile(regex)` and reuse `pattern.split(input)` to avoid recompilation. ## Practical guidance - Splitting on `.` `|` `\` etc. → escape or `Pattern.quote`. - Need all trailing fields → `split(regex, -1)`. - Need only the first N pieces with the remainder intact → positive limit. - Hot loop → precompile the Pattern.

  • You parse "x,y,," with split(",") and get only 2 elements. How do you get all 4 fields?
    Use a negative limit: "x,y,,".split(",", -1) → ["x", "y", "", ""]. The default limit 0 strips the two trailing empty strings; a negative limit keeps them.
  • Why does "a.b".split(".") return an empty array?
    '.' is a regex metacharacter meaning 'any character', so every position matches and every piece is empty. With the default limit 0, all those empties (which are trailing) are stripped, leaving an empty array. Use split("\\.") for a literal dot.

saying these in an interview costs you the question

  • Calling split(".") expecting a literal dot split
  • Assuming trailing empty fields are preserved by default
  • Thinking limit just caps size without affecting empty-string handling
  • Believing leading empties are dropped like trailing ones
  • Recompiling the regex in a tight loop instead of precompiling Pattern

context