Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bug in Pattern.asPredicate?

Tags:

java

regex

java-8

Given following list of Strings:

List<String> progLangs = Arrays.asList("c#", "java", "python", "scala");

and a regex Pattern that should match 4-letter lowercase string.

Pattern p = Pattern.compile("[a-z]{4}");

Now I want to find elements of progLangs that fit to the Pattern p.

Doing it the old way:

for (String lang : progLangs) {
    if (p.matcher(lang).matches()) {
        System.out.println(lang);
    }
}

I get the expected result:

java

But when I try to achieve the same with Java 8 stream and transform the pattern to a predicate using Pattern.asPredicate:

progLangs.stream()
    .filter(p.asPredicate())
    .forEach(System.out::println);

the result is:

java
python
scala

Why is it so? It seems that Patter.asPredicate produces a predicate that accepts partial matches. What is the equivalent in the Pattern API? The documentation says only:

Creates a predicate which can be used to match a string.

I would expect it to be the typical Pattern.matcher(String).matches() but it's something else... How to explain this inconsistency?

like image 1000
Lukasz Wiktor Avatar asked May 08 '14 11:05

Lukasz Wiktor


2 Answers

With JDK/11, you can use the new Pattern.asMatchPredicate API to accomplish what you were initially trying to, in a one-liner as :

progLangs.stream()
         .filter(p.asMatchPredicate()) // the matches predicate
         .forEach(System.out::println);

Here is what the javadoc for the same reads :

/**
 * Creates a predicate that tests if this pattern matches a given input string.
 *
 * @apiNote
 * This method creates a predicate that behaves as if it creates a matcher
 * from the input sequence and then calls matches, for example a
 * predicate of the form:
 *   s -> matcher(s).matches();
 *
 * @return  The predicate which can be used for matching an input string
 *          against this pattern.
 * @since   11
 * @see     Matcher#matches
 */
public Predicate<String> asMatchPredicate() {
    return s -> matcher(s).matches();
}
like image 159
Naman Avatar answered Oct 02 '22 14:10

Naman


They are not doing the same thing - the Predicate uses find rather than matches. The equivalent 'old code' way would be:

for (String lang : progLangs) {
    if (p.matcher(lang).find()) {
        System.out.println(lang);
    }
}

In this case I would use my own predicate:

progLangs.stream()
    .filter(s -> p.matcher(s).matches())
    .forEach(System.out::println);

The documentation does seem misleading though.

like image 32
Jonathan Avatar answered Oct 02 '22 14:10

Jonathan