收集HashSet / Java 8 / Regex Pattern / Stream API
最近我更改了JDK 8的版本而不是我的项目中的7个,现在我使用Java 8附带的新function覆盖了一些代码片段。
final Matcher mtr = Pattern.compile(regex).matcher(input); HashSet set = new HashSet() {{ while (mtr.find()) add(mtr.group().toLowerCase()); }};
我如何使用Stream API编写此代码?
如果重用JDK提供的Spliterators.AbstractSpliterator
基于Matcher
的spliterator实现可以非常简单:
public class MatcherSpliterator extends AbstractSpliterator { 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; } }
请注意,spliterator提供所有匹配器组,而不仅仅是完全匹配。 另请注意,此spliterator支持并行性,因为AbstractSpliterator
实现了拆分策略。
通常,您将使用便利流工厂:
public static Stream 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 set = matcherStream(mtr).map(gs->gs[0].toLowerCase()).collect(toSet());
Marko的回答演示了如何使用Spliterator
将匹配项添加到流中。 干得好,给那个男人一个大+1! 说真的,确保你在你考虑提出这个问题之前先回答他的答案,因为这个问题完全是他的。
我只有一小部分要添加到Marko的答案中,而不是将匹配表示为字符串数组(每个数组元素代表一个匹配组),匹配更好地表示为MatchResult
,这是一种发明的类型这个目的。 因此,结果将是Stream
而不是Stream
。 代码也变得更简单了。 tryAdvance
代码将是
if (m.find()) { action.accept(m.toMatchResult()); return true; } else { return false; }
他的电子邮件匹配示例中的map
调用将更改为
.map(mr -> mr.group(2))
并且OP的例子将被重写为
Set set = matcherStream(mtr) .map(mr -> mr.group(0).toLowerCase()) .collect(toSet());
使用MatchResult
提供更多的灵活性,因为它还提供了字符串中匹配组的偏移量,这对某些应用程序非常有用。
我不认为你可以在没有编写自己的Spliterator的情况下将其变成Stream
,但是,我不知道你为什么要这样做。
Matcher.find()
是对Matcher
对象的状态更改操作,因此在并行流中运行每个find()会产生不一致的结果。 串行运行流不会具有Java 7等效的更好的性能,并且难以理解。
那么Pattern.splitAsStream
呢?
Stream stream = Pattern.compile(regex).splitAsStream(input);
然后是collections家来获得一套。
Set set = stream.map(String::toLowerCase).collect(Collectors.toSet());
关于什么
public class MakeItSimple { public static void main(String[] args) throws FileNotFoundException { Scanner s = new Scanner(new File("C:\\Users\\Admin\\Desktop\\TextFiles\\Emails.txt")); HashSet set = new HashSet<>(); while ( s.hasNext()) { String r = s.next(); if (r.matches("([^,]+?)@([^,]+)")) { set.add(r); } } set.stream().map( x -> x.toUpperCase()).forEach(x -> print(x)); s.close(); } }
这是使用Spliterator接口的实现。
// To get the required set Set result = (StreamSupport.stream(new MatcherGroupIterator(pattern,input ),false)) .map( s -> s.toLowerCase() ) .collect(Collectors.toSet()); ... private static class MatcherGroupIterator implements Spliterator { private final Matcher matcher; public MatcherGroupIterator(Pattern p, String s) { matcher = p.matcher(s); } @Override public boolean tryAdvance(Consumer super String> action) { if (!matcher.find()){ return false; } action.accept(matcher.group()); return true; } @Override public Spliterator trySplit() { return null; } @Override public long estimateSize() { return Long.MAX_VALUE; } @Override public int characteristics() { return Spliterator.NONNULL; } }