Issue
I've read about match
vs find
on StackOverfow and appended a + to my regex.
It seems like can't use find()
because when I'm trying to utilize Stream API. I don't know how to use find()
with Streams.
These are the lines I am applying regex on: https://regex101.com/r/THZpM6/1
This is my code without no streams, which is working correctly:
String regex_class = "\"class[0-9]+\"+";
List<String> matches = new ArrayList<>();
Pattern p = Pattern.compile(regex_class);
for(String line : logEntries) {
Matcher m = p.matcher(line);
if(m.find())
matches.add(m.group());
}
System.out.println(matches); // ["class0", "class1", "class2", "class3"]
I was trying to use streams, but the results don't match the results I was expecting. Both generated lists are empty.
My attempts:
String regex_class = "\"class[0-9]+\"+";
// 1st attempt
final Pattern p = Pattern.compile(regex_class);
List<String> result_line1 = logEntries.stream()
.filter(e -> p.matcher(e).matches())
.collect(Collectors.toList());
System.out.println("result_line1 " + result_line1.size()); // size is 0
// 2nd attempt
List<String> result_class = logEntries.stream()
.filter(line -> line.matches(regex_class))
.collect(Collectors.toList());
System.out.println("result_class " + result_class.size()); // size is 0
Solution
You can use Matcher.find()
successfully with streams.
@shmosel has provided one of the ways to do that in this comment. Here's the solution he proposed (formatted and sprinkled with comments):
List<String> logEntries = // initializing the list
Pattern p = Pattern.compile("\"class\\d+\"+");
List<String> result_class = logEntries.stream() // Stream<String>
.map(p::matcher) // Stream<Matcher>
.filter(Matcher::find) // Stream<Matcher>
.map(Matcher::group) // Stream<String>
.collect(CollectrotoList());
Alternatively, we can use Java 16 mapMulty()
combine to perform filtering and mapping in a single step:
List<String> logEntries = // initializing the list
Pattern p = Pattern.compile("\"class\\d+\"+");
List<String> matches = logEntries.stream()
.map(p::matcher)
.<String>mapMulti((matcher, consumer) -> {
if (matcher.find()) consumer.accept(matcher.group());
})
.toList();
Your attempts were not successful, because the methods you're using with streams and for
-loop have different behavior.
Method from the first stream Matcher.matches()
Attempts to match the entire region against the pattern.
Method String.matches()
behaves in the same way.
I.e. effectively, you're matching the stream elements against the following regular expression "^\"class[0-9]+\"+$"
.
But in the snippet with a for
-loop you're using Matcher.find()
, which "attempts to find the next subsequence of the input sequence that matches the pattern", i.e. it's looking for a matching subsequence and (which is not necessarily should the entire string).
Sidenote: as @Holger has pointed out in the comments, +
quantifier looks fishy. If the whole sequence "class'Number'"
can be repeated multiple times, then you need to enclose it in parentheses "(\"class\\d+\")+"
.
Answered By - Alexander Ivanchenko
Answer Checked By - Marilyn (JavaFixing Volunteer)