Pattern.asPredicate中存在Bug吗?

14
给定以下字符串列表:
List<String> progLangs = Arrays.asList("c#", "java", "python", "scala");

还有一个正则表达式模式,应该匹配4个小写字母字符串。

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

现在我想找到与模式 p 匹配的 progLangs 元素。

按照旧的方法进行:

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生成的谓词接受部分匹配。在Pattern API中有等价的方法吗?documentation只说了这个:
创建一个可以用来匹配字符串的谓词。
我本以为它是典型的Pattern.matcher(String).matches(),但实际上不是......如何解释这种不一致性呢?

1
使用JDK/11,matches可用,您的初始代码实现了您想要的功能,只是方法调用发生了变化。 :) - Naman
2个回答

15

他们不是在做同样的事情- 谓词使用find而不是matches。 相当于“旧代码”的方式是:

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

不过,文件确实给人一种误导的感觉。


看起来你说得对。但是,我仍然不明白为什么他们要用这种方式实现它。Matcher.find() 的使用真的比 Matcher.matches() 更常见吗? - Lukasz Wiktor
我无法确定,也许我漏掉了什么。 - Jonathan
10
@LukaszWiktor,实际上选择“find”还是“match”并不那么重要,因为我们可以编写正则表达式使“find”的行为像“matches”,方法是在其周围添加“^”和“$”,就像您的情况中的“^[a-z]{4}$”一样;或者通过使用“.”将正则表达式包围起来,使“matches”的行为像“find”(仅调用一次),例如“. searchedPattern .”。也许选择“find”的原因是它更快,因为它在找到第一个匹配子字符串后就会停止迭代,而不像“. regex .*”一样遍历整个字符串。 - Pshemo
LukaszWiktor提出了非常好的观点。您应该编辑答案,以确保引起人们的注意。 - Alexandros
2
现在又有一个答案,使用matches也可以。 - Naman

7

使用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();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接