Java 从头开始或从迭代器创建流

Java 从头开始或从迭代器创建流,java,java-8,java-stream,Java,Java 8,Java Stream,我想不出一个好办法来从头开始创建一个。例如,假设注意,下面的代码只是为了讨论而提供的一个示例 Matcher m = Pattern.compile(re).matcher(input); List<String> matches = new ArrayList<>(); while (m.find()) matches.add(m.group()); 但是我也找不出一种简单的方法来用迭代器创建流 编辑:我意识到我可以创建一个Iterable,创建匹配迭代器,然

我想不出一个好办法来从头开始创建一个。例如,假设注意,下面的代码只是为了讨论而提供的一个示例

Matcher m = Pattern.compile(re).matcher(input);
List<String> matches = new ArrayList<>();
while (m.find())
    matches.add(m.group());
但是我也找不出一种简单的方法来用迭代器创建流


编辑:我意识到我可以创建一个Iterable,创建匹配迭代器,然后使用StreamSupport/Spliterator,但这需要我能够在源代码上迭代多次,因此,它仍然不是一个通用的解决方案。

如果您可以重新格式化正则表达式以指定边界,而不是您可能会看到的匹配项。我没有找到类似的解决方案来获得匹配流,所以我做了一个,它将在这个答案的末尾出现

此解决方案将允许创建流而不是流,因为使用.mapMatchResult::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
当然,使用MatchSpliterator.streampattern,input.mapMatchResult::group.collectCollectors.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;
    }
}
要生成流,您需要一个拆分器。然后使用StreamSupport.Stream生成流。所有的收藏都是这样做的

如果你已经有了一个Iterable,你可以从它的Spliterator方法中得到一个Spliterator,尽管你可能想写一个更好的;违约率很低

如果您有一个迭代器,您可以将其转换为带有拆分器的拆分器。拆分器未知迭代器。同样,这会得到一个拆分器,但不一定是最佳拆分器


如果两者都没有,您可以编写一个拆分器;它通常比编写迭代器更容易实现,但通常更简单,因为您不必在next和hasNext之间复制逻辑

非常奇怪的是,没有一个简单的抽象流实现具有类似于普通迭代器的抽象方法。似乎hasNext/next足以实现一个基本流。实际上,我发现tryAdvance对于这样的用例更方便,因为逻辑包含在一个方法中,而不是分散在两个方法和构造函数中。除了我为方便使用而添加的附加工厂方法外,实现拆分器只需要三个小方法。我不认为这比实现迭代器更复杂。注意,你可以…啊,有趣的点。我不知道Spliterator Unknownsize方法!谢谢你,布莱恩。Spliterators类和spliteratorUnknownSize是我的拼图中缺少的部分。tryAdvance应该将下一个值推送到使用者,这一事实在用于实现好的旧迭代器时也有点令人困惑。但我同意,一旦习惯了这一点,可以说实现拆分器更容易。
match: first
match: second item
match: third
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;
    }
}