Bug in Pattern.asPredicate?

2022-09-04 01:34:28

给定以下字符串列表:

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

和应匹配 4 个字母小写字符串的正则表达式模式。

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

现在我想找到适合模式的元素。progLangsp

以旧的方式做:

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

我得到预期的结果:

java

但是,当我尝试使用 Java 8 流实现相同的目标并使用 Pattern.asPredicate 将模式转换为谓词时

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

结果是:

java
python
scala

为什么会这样?Patter.asPredicate 似乎生成了一个接受部分匹配的谓词。模式 API 中的等效项是什么?文档仅显示:

创建可用于匹配字符串的谓词。

我希望它是典型的,但它是另一回事...如何解释这种不一致?Pattern.matcher(String).matches()


答案 1

他们没有做同样的事情 - 谓词使用查找而不是匹配。等效的“旧代码”方式是:

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

在这种情况下,我将使用我自己的谓词:

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

不过,文档似乎确实具有误导性。


答案 2

使用 JDK/11,您可以使用新的 Pattern.asMatchPredicate API 来完成您最初尝试完成的工作,只需一行即可:

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

以下是相同的javadoc的内容:

/**
 * 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();
}