Using the Java Matcher Class for Pattern Matching
java matcher class: Learn how to use the Java Matcher class for regex pattern matching, including find, matches, groups, and performance considerations.
The Java Matcher class is the engine that applies a compiled regular expression to a character sequence. You never construct a Matcher directly; instead, you obtain one from a Pattern instance via the matcher(CharSequence) method. This separation keeps pattern compilation separate from matching, which matters when the same pattern is used repeatedly.
Pattern pattern = Pattern.compile("\\d+"); Matcher matcher = pattern.matcher("Order 12345");
Once you have a Matcher, you can run different matching operations on the same input. The Matcher holds the state of the last successful match, which affects how subsequent calls behave.
Creating a Matcher from a Pattern
The Pattern.matcher() method returns a new Matcher bound to the input sequence you provide. The pattern itself is not modified, so you can create multiple matchers from the same Pattern for different inputs.
Pattern digitPattern = Pattern.compile("\\d+"); Matcher first = digitPattern.matcher("abc123"); Matcher second = digitPattern.matcher("456def");
Each Matcher maintains its own region and match state. If you need to reuse a single Matcher for a different input, call reset(CharSequence) to rebind it. This avoids creating a new object when you are processing many strings in a loop.
Using find() and matches()
The two most common methods are find() and matches(). They serve different purposes and produce different results for the same pattern and input.
find()scans the input for the next subsequence that matches the pattern. It returnstrueif a match exists, and advances the matcher's position to just after that match.matches()requires the entire input to match the pattern. It returnstrueonly if the whole region matches, and it does not advance the position in the same way.
Consider the pattern \\d+ on the input "abc123":
Matcher m = Pattern.compile("\\d+").matcher("abc123"); System.out.println(m.matches()); // false, because "abc" is not digits m.reset(); // reset to beginning System.out.println(m.find()); // true, finds "123"
Use matches() when you need to validate that an entire string conforms to a format, such as a phone number or email. Use find() when you need to locate substrings that match, such as extracting all numbers from a log line.
Working with Capturing Groups
Capturing groups let you extract specific parts of a matched substring. Groups are defined by parentheses in the pattern. The group with index 0 is the entire match; groups 1, 2, etc., correspond to each pair of parentheses from left to right.
Pattern p = Pattern.compile("(\\d+)-(\\d+)"); Matcher m = p.matcher("Range: 10-20"); if (m.find()) { System.out.println(m.group(0)); // "10-20" System.out.println(m.group(1)); // "10" System.out.println(m.group(2)); // "20" }
The group(int) method returns the captured text for that group. If a group did not participate in the match, it returns null. For example, with an optional group like (\\d+)?, a match might not include that group.
Using start() and end() for Positions
In addition to the text, you often need the positions where a match or group begins and ends. The start() and end() methods return the indices into the original input. The end() index is exclusive, so the length of the match is end() - start().
Matcher m = Pattern.compile("cat").matcher("A cat and a dog"); while (m.find()) { System.out.println("Match at " + m.start() + " to " + m.end()); }
This is useful when you need to replace or annotate matched regions without reconstructing the string manually.
Handling Multiple Matches with find() in a Loop
A common task is to process every occurrence of a pattern. The find() method advances the matcher each time it is called, so a simple loop works:
Pattern p = Pattern.compile("\\b\\w+\\b"); Matcher m = p.matcher("one two three"); while (m.find()) { System.out.println(m.group()); }
Be careful when you modify the input inside the loop. The matcher's region and position are based on the original input; changing the string invalidates the match positions. If you need to transform text, use Matcher.replaceAll() or appendReplacement() instead.
Performance Considerations: Reusing Matcher Instances
Compiling a regex is expensive because it builds an internal state machine. The Matcher itself is lightweight, but it holds state. Reusing a Matcher with reset() avoids recompiling the pattern and reduces object allocation.
Pattern p = Pattern.compile("\\d+"); Matcher m = p.matcher(""); for (String line : lines) { m.reset(line); while (m.find()) { // process match } }
This is especially relevant in high-throughput scenarios, such as parsing network traffic or log files, where the same pattern is applied to thousands of lines. The performance gain comes from not recreating the Pattern and not allocating a new Matcher for each line.
Common Pitfalls with Matcher State
The Matcher is stateful. After a find() returns true, the matcher's position is at the end of the match. A subsequent call to matches() will not behave as you might expect because it operates from the current position unless you call reset(). Similarly, calling group() after a failed match throws IllegalStateException.
Matcher m = Pattern.compile("\\d+").matcher("abc"); if (m.find()) { // safe to call group() } else { // m.group() would throw IllegalStateException }
Always check the return value of find() or matches() before accessing groups or positions. If you need to reuse the matcher for a different input, call reset() first to clear the previous match state.
When to Use Matcher Instead of String Methods
The String class offers matches(), split(), and replaceAll() that internally use a Matcher. These are convenient for one-off operations, but they compile the pattern each time. If you are performing the same regex operation many times, using a precompiled Pattern and a Matcher gives you control over reuse and access to group details that String methods do not expose.
For simple literal replacements, String.replace() is more efficient than a regex. Reserve the Matcher for cases where you need pattern semantics, capturing groups, or the ability to iterate over multiple matches. Understanding the Java Matcher class gives you the tools to build efficient, readable text-processing code without hiding the underlying regex engine's behavior.