1. 導入:なぜMatcher.results()が重要なのか
Javaで正規表現を扱う際、従来はwhileループとmatcher.find()を組み合わせて記述するのが一般的でした。しかし、この方法ではループの制御や状態管理が煩雑になりがちです。Java 9で導入されたMatcher.results()を使えば、マッチした結果をStream APIとして扱えるようになり、コードをより宣言的で読みやすく書くことができます。複雑な文字列処理をシンプルに解決したい方にぴったりのテクニックです。
2. 基礎知識:正規表現とストリーム
Javaで正規表現を扱うには、主に「Patternクラス」と「Matcherクラス」を使用します。
・Pattern:正規表現のコンパイル済み表現です。
・Matcher:Patternを使って文字列を照合するエンジンです。
・MatchResult:マッチした個々の結果を保持するインターフェースで、グループごとの値や位置情報を取得できます。
Java 9以降、Matcherのresults()メソッドを呼び出すことで、これらの一致結果をStreamとしてストリーム処理(mapやfilterなど)に直接流し込むことが可能になりました。
3. 実装と解決策
Matcher.results()を活用すると、マッチした結果に対して「抽出」「変換」「集計」を一行で行えます。従来のように「見つかったらリストに詰め込む」といった定型的な処理を書く必要がなくなり、バグの混入リスクも減らせます。特に名前付きグループ(Named groups)と組み合わせることで、可読性の高いデータ抽出が可能になります。
4. サンプルプログラム
以下のコードは、文字列から名前付きグループを使って「名前」と「年齢」を抽出し、リスト化する例です。
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.List;
public class RegexStreamExample {
public static void main(String[] args) {
String text = "田中:25, 佐藤:30, 鈴木:22";
// 名前付きグループを使用してパターンを定義 (?<名前>...)
Pattern pattern = Pattern.compile("(?<name>[^:]+):(?<age>\\d+)");
Matcher matcher = pattern.matcher(text);
// results()でStream<MatchResult>を取得して処理
List<String> results = matcher.results()
.map(mr -> mr.group("name") + "さんは" + mr.group("age") + "歳")
.collect(Collectors.toList());
// 結果を出力
results.forEach(System.out::println);
}
}
5. 応用・注意点
・ストリームの消費に注意:Matcher.results()が生成するストリームは、内部のMatcherの状態に依存しています。一度消費したストリームを再利用することはできないため、必要な場合はcollectなどで一度コレクションに格納してください。
・名前付きグループの活用:インデックス番号(group(1)など)でアクセスすると、正規表現を変更した際に修正漏れが発生しやすくなります。可能な限り名前付きグループを使用し、保守性の高いコードを心がけましょう。
・パフォーマンス:非常に巨大なテキストに対してストリーム処理を行う場合、メモリ使用量に注意が必要です。必要に応じてlimit()を活用し、不要な全件走査を避ける工夫も大切です。
この手法をマスターすれば、ログ解析やデータクレンジングのコードが驚くほどスッキリします。ぜひ現場のプロジェクトでも活用してみてください。

コメント