Home > Mobile >  Trying to obtain a matching subtring for each element in a List using Stream API
Trying to obtain a matching subtring for each element in a List using Stream API

Time:11-05

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 am trying to use streams, but the result not matching anything.

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

CodePudding user response:

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[0-9] \" ");

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[0-9] \" ");

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).

  • Related