Collect HashSet / Java 8 / Regex Pattern / Stream API

2022-09-03 16:43:51

最近,我更改了 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());
}};

如何使用流 API 编写此代码?


答案 1

基于拆分器的实现可能非常简单,如果您重用 JDK 提供的:MatcherSpliterators.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;
  }
}

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

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

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

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

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(", ")));
}

哪些打印件

User has e-mail accounts on these domains: gmail.com, yahoo.com, tijuana.com

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

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

答案 2

Marko 的答案演示了如何使用 .干得好,给那个男人一个大+1!说真的,在你考虑投票之前,一定要先给他的回答投赞成票,因为这个答案完全是他的衍生物。Spliterator

我对Marko的答案只有一点点补充,那就是不是将匹配项表示为字符串数组(每个数组元素表示一个匹配组),而是更好地表示为为此目的发明的类型。因此,结果将是 代替 。代码也变得简单了一点。代码将是MatchResultStream<MatchResult>Stream<String[]>tryAdvance

    if (m.find()) {
        action.accept(m.toMatchResult());
        return true;
    } else {
        return false;
    }

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

    .map(mr -> mr.group(2))

OP的例子将被重写为

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

使用可以提高灵活性,因为它还提供了字符串中匹配组的偏移量,这可能对某些应用程序有用。MatchResult