skip to content

How do named capturing groups work in Java regex, and how do you reference them?

level: middleimportance: should knowfreq 55%

answer

  1. (?<name>...) defines, group("name") reads
  2. \k<name> backreference inside pattern
  3. ${name} in replacement strings
  4. names still have a number too; must be unique; Java 7+

basics

~10 s

Write (?<name>...) to give a capturing group a name. Read it back with matcher.group("name") instead of a number, and refer to it inside the pattern with \k<name>. Names make patterns readable.

solid answer

~40 s

Java supports named capturing groups with the syntax (?<name>...), where name is letters and digits starting with a letter. The group still gets a normal number too, so group(1) and group("name") may both work, but you can now retrieve it by name via matcher.group("name"), and there are start("name")/end("name") overloads. Inside the same pattern you backreference it with \k<name>, and in replacement strings you reference it with ${name}. The big win is readability and robustness: ${name} and group("name") don't break when you insert or remove other groups, unlike positional indices that all shift. Group names must be unique within the pattern. Asking for an unknown name throws IllegalArgumentException. Named groups were added in Java 7; before that only numeric groups existed.

code

java · 9 lines
java
Pattern p = Pattern.compile("(?<year>\\d{4})-(?<month>\\d{2})");
Matcher m = p.matcher("2026-06");
if (m.matches()) {
    System.out.println(m.group("year"));   // 2026
    System.out.println(m.group("month"));  // 06
}
// Replacement uses ${name}:
String r = "2026-06".replaceAll("(?<y>\\d{4})-(?<m>\\d{2})", "${m}/${y}");
System.out.println(r); // 06/2026

go deeper

for a junior

Knows (?<name>...) defines a named group read with group("name").

for a middle

Adds \k<name> backreferences and ${name} in replacements, plus uniqueness and Java 7+ availability.

for a senior

Argues for named groups in complex patterns for robustness against renumbering and self-documentation; knows the exception behavior.

for a principal

Sets team conventions, weighs readability/maintainability trade-offs, and considers cross-language portability of the named-group syntax.

## Starting point: numeric groups A capturing group `( ... )` remembers what it matched and is identified by a **number** (1, 2, 3...) based on the position of its opening parenthesis. You read it with `matcher.group(1)`. This works but has a weakness: the number is **positional**, so inserting or deleting an earlier group renumbers everything after it, quietly breaking `group(3)` or `$3`. ## Named capturing groups A **named capturing group** attaches a human-readable label. The syntax is: ``` (?<name>...) ``` The `?<name>` sits right after the opening parenthesis. The **name** must start with a letter and contain only letters and digits, and must be **unique** within the pattern. Example: `(?<year>\d{4})-(?<month>\d{2})` against `2026-06`. ## Reading a named group After a successful match: - `matcher.group("year")` → `2026` - `matcher.group("month")` → `06` - `matcher.start("year")` / `matcher.end("year")` → its position bounds. A named group **also keeps a number**. So in the example `group(1)` equals `group("year")`. Naming is additive, not a replacement for numbering. ## Backreferencing a named group inside the pattern Use `\k<name>` to match the same text the named group captured earlier: ``` (?<q>['\"]).*?\k<q> ``` matches a quoted string where the closing quote equals the opening one. (The numeric equivalent would be `\1`.) ## Referencing a named group in a replacement In `replaceAll`/`replaceFirst`/`appendReplacement`, refer to a named group with `${name}`: ```java "2026-06".replaceAll("(?<y>\\d{4})-(?<m>\\d{2})", "${m}/${y}") // "06/2026" ``` (The numeric equivalent is `$1`, `$2`.) ## Errors and edge cases - Requesting an **unknown** name — `group("nope")` — throws `IllegalArgumentException`. - Calling `group("name")` before a successful match throws `IllegalStateException`, same as numeric. - A name may be used **at most once** per pattern; duplicate names are a `PatternSyntaxException` at compile time. - Named groups arrived in **Java 7**; earlier versions support only numeric groups. ## Why use them Named groups make complex patterns self-documenting and make the extraction code robust against pattern edits, because names don't shift the way positional indices do. For patterns with more than two or three captures, they are usually the better choice.

  • How do you backreference a named group inside the pattern, and inside a replacement string?
    Inside the pattern use \k<name>; inside a replacement string use ${name}.
  • Does a named group still have a numeric index?
    Yes. Naming is additive; the group keeps its positional number, so group(1) and group("name") can both reference it.

Numeric groups are like referring to coworkers by seat number — fine until someone moves desks. Named groups are like calling them by name: the reference stays valid no matter who sits where.

saying these in an interview costs you the question

  • Thinking naming removes the numeric index — it doesn't
  • Using $name or \name instead of ${name}/\k<name>
  • Reusing the same group name twice in one pattern (compile error)
  • Assuming named groups exist before Java 7

context