我无法找到从头开始创建Stream
的好方法。举例来说(注意,下面的代码只是为了讨论的例子)我有
Matcher m = Pattern.compile(re).matcher(input);
List<String> matches = new ArrayList<>();
while (m.find())
matches.add(m.group());
并希望利用流API。我想做点像
List<String> matches = Stream.of(() -> m.find(), () -> m.group())
.collect(Collectors.toList());
其中() -> m.find()
是一个函数,告诉我是否有更多元素,而() -> m.group()
是一个提供下一个元素的函数。
例如,创建Iterator
很容易:
class MatchIterator implements Iterator<String> {
Matcher m;
boolean hasNext;
public MatchIterator(Matcher m) {
this.m = m;
hasNext = m.find();
}
@Override
public boolean hasNext() {
return hasNext;
}
@Override
public String next() {
String next = m.group();
hasNext = m.find();
return next;
}
}
但我无法想出一种从迭代器中创建流的简单方法。
编辑:我意识到我可以创建Iterable
来创建MatchIterator
s(然后使用StreamSupport
/ Spliterator
),但这需要我能够迭代来源多次,所以它仍然不是一个通用的解决方案。
答案 0 :(得分:2)
如果您可以重新规划正则表达式以指定边界而不是匹配,那么您可以查看Pattern.splitAsStream
。我没有找到类似的解决方案来获得匹配流,所以我做了一个将在这个答案结束后跟进。
此解决方案将允许创建Stream<MatchResult>
而不是Stream<String>
,因为使用map
很容易.map(MatchResult::group)
整个匹配的这种流,但提供更大的灵活性。请参阅以下用例:
String testcase="first \"second item\" third";
MatchSpliterator.stream("\"([^\"]+)\"|\\S+", testcase)
.map(r->Optional.ofNullable(r.group(1)).orElseGet(r::group))
.forEach(s->System.out.println("match: "+s));
打印
match: first
match: second item
match: third
当然,使用List<String>
MatchSpliterator.stream(pattern, input) .map(MatchResult::group).collect(Collectors.toList());
是直截了当的
实现:
public class MatchSpliterator implements Spliterator<MatchResult> {
public static Stream<MatchResult> stream(String pattern, CharSequence input) {
return stream(Pattern.compile(pattern), input);
}
public static Stream<MatchResult> stream(Pattern p, CharSequence input) {
return stream(p.matcher(input));
}
public static Stream<MatchResult> stream(Matcher matcher) {
return StreamSupport.stream(new MatchSpliterator(matcher), false);
}
private final Matcher matcher;
private MatchSpliterator(Matcher m) {
matcher=m;
}
public boolean tryAdvance(Consumer<? super MatchResult> action) {
if(matcher.find()) {
action.accept(matcher.toMatchResult());
return true;
}
return false;
}
public Spliterator<MatchResult> trySplit() {
return null;
}
public long estimateSize() {
return Long.MAX_VALUE;
}
public int characteristics() {
return NONNULL|ORDERED;
}
}
答案 1 :(得分:2)
要制作Stream
,您需要Spliterator
。然后使用Stream
制作StreamSupport.stream()
。 (这就是所有收藏品所做的。)
如果您已经拥有Iterable
,则可以从Spliterator
方法获取spliterator()
(尽管您可能想要写一个更好的方法;默认为弱)。
如果您有Iterator
,则可以将其转为Spliterator
Spliterators.spliteratorUnknownSize(Iterator)
。 (同样,这会让你成为分裂者,但不一定是最佳分裂者。)
如果你没有,你可以写一个Spliterator
;它通常比编写Iterator
更容易(实现的方法更多,但通常更简单,因为您不必在next
和hasNext
之间复制逻辑。)