Pattern.asPredicate中的错误?

Luk*_*tor 9 java regex java-8

给出以下字符串列表:

List<String> progLangs = Arrays.asList("c#", "java", "python", "scala");
Run Code Online (Sandbox Code Playgroud)

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

Pattern p = Pattern.compile("[a-z]{4}");
Run Code Online (Sandbox Code Playgroud)

现在我想找到progLangs适合Pattern的元素p.

用旧方式做到这一点:

for (String lang : progLangs) {
    if (p.matcher(lang).matches()) {
        System.out.println(lang);
    }
}
Run Code Online (Sandbox Code Playgroud)

我得到了预期的结果:

java
Run Code Online (Sandbox Code Playgroud)

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

progLangs.stream()
    .filter(p.asPredicate())
    .forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

结果是:

java
python
scala
Run Code Online (Sandbox Code Playgroud)

为什么会这样?看起来Patter.asPredicate产生一个接受部分匹配的谓词.Pattern API中的等价物是什么?该文件只是说:

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

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

Jon*_*han 12

他们没有做同样的事情 - 谓词使用find而不是matches.等效的"旧代码"方式是:

for (String lang : progLangs) {
    if (p.matcher(lang).find()) {
        System.out.println(lang);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

progLangs.stream()
    .filter(s -> p.matcher(s).matches())
    .forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

但文档似乎确实有误导性.

  • @LukaszWiktor实际上选择`find`或`match`并不是那么重要,因为我们可以编写正则表达式,它可以使`find`表现得像`match`,在你的情况下添加`^`和`$`就像`^ [ az] {4} $`,或者使``matches`就像`find`(一次调用)一样,周围的正则表达式用`.*`就像`.*serchedPattern.*`.也许选择`find`是因为它更快,因为它会在找到第一个匹配的substirng后停止迭代,而不是像`.*regex.*`那样迭代整个字符串. (7认同)
  • 做了另一个答案,现在 [`matches` 可用](/sf/answers/3625415671/) 也是如此。 (2认同)

Nam*_*man 6

使用JDK/11,您可以使用新的Pattern.asMatchPredicateAPI 以单行方式完成您最初尝试做的事情:

progLangs.stream()
         .filter(p.asMatchPredicate()) // the matches predicate
         .forEach(System.out::println);
Run Code Online (Sandbox Code Playgroud)

以下是相同内容的 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();
}
Run Code Online (Sandbox Code Playgroud)