收集HashSet/Java 8/Regex Pattern/Stream API

Ant*_*sev 14 java regex collections java-8 java-stream

最近我更改了JDK 8的版本而不是我的项目中的7个,现在我使用Java 8附带的新功能覆盖了一些代码片段.

final Matcher mtr = Pattern.compile(regex).matcher(input);

HashSet<String> set = new HashSet<String>() {{
    while (mtr.find()) add(mtr.group().toLowerCase());
}};
Run Code Online (Sandbox Code Playgroud)

我如何使用Stream API编写此代码?

Mar*_*nik 26

Matcher如果重用JDK提供的,基于A 的spliterator实现可以非常简单Spliterators.AbstractSpliterator:

public class MatcherSpliterator extends AbstractSpliterator<String[]>
{
  private final Matcher m;

  public MatcherSpliterator(Matcher m) {
    super(Long.MAX_VALUE, ORDERED | NONNULL | IMMUTABLE);
    this.m = m;
  }

  @Override public boolean tryAdvance(Consumer<? super String[]> action) {
    if (!m.find()) return false;
    final String[] groups = new String[m.groupCount()+1];
    for (int i = 0; i <= m.groupCount(); i++) groups[i] = m.group(i);
    action.accept(groups);
    return true;
  }
}
Run Code Online (Sandbox Code Playgroud)

请注意,spliterator提供所有匹配器组,而不仅仅是完全匹配.另请注意,此spliterator支持并行性,因为它AbstractSpliterator实现了拆分策略.

通常,您将使用便利流工厂:

public static Stream<String[]> matcherStream(Matcher m) {
  return StreamSupport.stream(new MatcherSpliterator(m), false);
}
Run Code Online (Sandbox Code Playgroud)

这为您简明地编写各种复杂的面向正则表达式逻辑提供了强大的基础,例如:

private static final Pattern emailRegex = Pattern.compile("([^,]+?)@([^,]+)");
public static void main(String[] args) {
  final String emails = "kid@gmail.com, stray@yahoo.com, miks@tijuana.com";
  System.out.println("User has e-mail accounts on these domains: " +
      matcherStream(emailRegex.matcher(emails))
      .map(gs->gs[2])
      .collect(joining(", ")));
}
Run Code Online (Sandbox Code Playgroud)

哪个打印

User has e-mail accounts on these domains: gmail.com, yahoo.com, tijuana.com
Run Code Online (Sandbox Code Playgroud)

为了完整起见,您的代码将被重写为

Set<String> set = matcherStream(mtr).map(gs->gs[0].toLowerCase()).collect(toSet());
Run Code Online (Sandbox Code Playgroud)

  • 注意:API`Matcher.results()`返回`Stream <MatchResult>`已集成到JDK 9中:https://bugs.openjdk.java.net/browse/JDK-8071479 (4认同)
  • 做得很好!如果像这样的东西被迁移到`Matcher` API本身就好了. (2认同)

Stu*_*rks 9

Marko的回答演示了如何使用a将匹配项添加到流中Spliterator.干得好,给那个男人一个大+1!说真的,在你考虑提出这个问题之前,请确保你提出他的答案,因为这个完全是他的.

我只有一小部分要添加到Marko的答案中,而不是将匹配表示为字符串数组(每个数组元素代表一个匹配组),匹配更好地表示为一个MatchResult为此发明的类型目的.因此结果将是一个Stream<MatchResult>而不是Stream<String[]>.代码也变得更简单了.该tryAdvance代码将是

    if (m.find()) {
        action.accept(m.toMatchResult());
        return true;
    } else {
        return false;
    }
Run Code Online (Sandbox Code Playgroud)

map他的电子邮件匹配示例中的调用将更改为

    .map(mr -> mr.group(2))
Run Code Online (Sandbox Code Playgroud)

并且OP的例子将被重写为

Set<String> set = matcherStream(mtr)
                      .map(mr -> mr.group(0).toLowerCase())
                      .collect(toSet());
Run Code Online (Sandbox Code Playgroud)

使用MatchResult提供了更多的灵活性,因为它还提供了字符串中匹配组的偏移量,这对某些应用程序非常有用.

  • +1并感谢赞美:)我从未注意到`Matcher` API的`MatchResult`部分,它绝对是要走的路. (2认同)
  • 注意:API`Matcher.results()`返回`Stream <MatchResult>`已集成到JDK 9中:https://bugs.openjdk.java.net/browse/JDK-8071479 (2认同)

dka*_*zel 8

我不认为你可以把它变成Stream没有写自己的Spliterator,但是,我不知道你为什么要这样做.

Matcher.find()Matcher对象的状态更改操作,因此在并行流中运行每个find()会产生不一致的结果.串行运行流将没有Java 7等效的更好的性能,并且将更难理解.

  • 我认为基于匹配器的Stream将是一个非常受欢迎的功能.例如,Clojure提供`re-seq`作为用于正则表达式处理的主要原语. (2认同)