Java 如何从 Iterator<E> 创建无限 Stream<E>?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21956515/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 11:38:15  来源:igfitidea点击:

How to create an infinite Stream<E> out of an Iterator<E>?

javaiteratorjava-8java-stream

提问by skiwi

Looking at the following class I've made:

查看我制作的以下课程:

public class FibonacciSupplier implements Iterator<Integer> {
    private final IntPredicate hasNextPredicate;

    private int beforePrevious = 0;
    private int previous = 1;

    private FibonacciSupplier(final IntPredicate hasNextPredicate) {
        this.hasNextPredicate = hasNextPredicate;
    }

    @Override
    public boolean hasNext() {
        return hasNextPredicate.test(previous);
    }

    @Override
    public Integer next() {
        int result = beforePrevious + previous;
        beforePrevious = previous;
        previous = result;
        return result;
    }

    public static FibonacciSupplier infinite() {
        return new FibonacciSupplier(i -> true);
    }

    public static FibonacciSupplier finite(final IntPredicate predicate) {
        return new FibonacciSupplier(predicate);
    }
} 

And the usage of it in:

以及它的用法:

public class Problem2 extends Problem<Integer> {
    @Override
    public void run() {
        result = toList(FibonacciSupplier.finite(i -> (i <= 4_000_000)))
                .stream()
                .filter(i -> (i % 2 == 0))
                .mapToInt(i -> i)
                .sum();
    }

    @Override
    public String getName() {
        return "Problem 2";
    }

    private static <E> List<E> toList(final Iterator<E> iterator) {
        List<E> list = new ArrayList<>();
        while (iterator.hasNext()) {
            list.add(iterator.next());
        }
        return list;
    }
}

How would I be able to create an infiniteStream<E>?

我怎样才能创造无限Stream<E>

If I were to use Stream<Integer> infiniteStream = toList(FibonacciSupplier.infinite()).stream(), I would, possibly surprisingly, never get an infinite stream.
Instead the code would loop forever in the creation of the listin an underlying method.

如果我使用Stream<Integer> infiniteStream = toList(FibonacciSupplier.infinite()).stream(),我可能会出人意料地永远不会得到无限流。
相反,代码将list在底层方法的创建中永远循环。

This so far is purely theoretical, but I can definately understand the need for it if I would want to first skip the first x numbers from an infinite stream, and then limit it by the last y numbers, something like:

到目前为止,这纯粹是理论上的,但如果我想首先跳过无限流中的前 x 个数字,然后通过最后 y 个数字限制它,我绝对可以理解它的必要性,例如:

int x = MAGIC_NUMBER_X;
int y = MAGIC_NUMBER_y;
int sum = toList(FibonacciSupplier.infinite())
    .stream()
    .skip(x)
    .limit(y)
    .mapToInt(i -> i)
    .sum();

The code would not ever return a result, how should it be done?

代码永远不会返回结果,应该怎么做?

回答by nosid

In Java 8 there are no public, concrete classesimplementing the interface Stream. However, there are some static factory methods. One of the most important is StreamSupport.stream. In particular, it is used in the default methodCollection.stream–inherited by most collection classes:

在 Java 8 中,没有实现接口Stream 的公共的、具体的类。但是,有一些静态工厂方法。最重要的一个是StreamSupport.stream。特别是,它被用在默认方法Collection.stream 中——大多数集合类都继承了它:

default Stream<E> stream() {
    return StreamSupport.stream(spliterator(), false);
}

The default implementation of this method creates a Spliteratorby invoking spliterator(), and passes the created object to the factory method. Spliteratoris a new interface introduced with Java 8 to support parallel streams. It is similar to Iterator, but in contrast to the latter, a Spliteratorcan be divided into parts, that can be processed independently. See Spliterator.trySplitfor details.

此方法的默认实现通过调用创建一个Spliteratorspliterator(),并将创建的对象传递给工厂方法。Spliterator是 Java 8 中引入的一个新接口,用于支持并行流。它类似于Iterator,但与后者相反,Spliterator可以分为多个部分,可以独立处理。有关详细信息,请参阅Spliterator.trySplit

The default methodIterable.spliteratorwas also added in Java 8, so that every Iterableclass automatically supports Spliterators. The implementation looks as follows:

默认方法Iterable.spliterator也被加入在Java中8,让每一个可迭代类自动支持Spliterators。实现如下所示:

default Spliterator<T> spliterator() {
    return Spliterators.spliteratorUnknownSize(iterator(), 0);
}

The method creates the Spliteratorfrom an arbitrary Iterator. If you combine these two steps, you can create a Streamfrom an arbitrary Iterator:

该方法从任意Iterator创建Spliterator。如果结合这两个步骤,您可以从任意Iterator创建一个Stream

<T> Stream<T> stream(Iterator<T> iterator) {
    Spliterator<T> spliterator
        = Spliterators.spliteratorUnknownSize(iterator, 0);
    return StreamSupport.stream(spliterator, false);
}


To get an impression of Spliterators, here is a very simple example without using collections. The following class implements Spliteratorto iterate over a half-open interval of integers:

为了获得Spliterators的印象,这里有一个非常简单的例子,没有使用集合。以下类实现了Spliterator以迭代整数的半开区间:

public final class IntRange implements Spliterator.OfInt {
    private int first, last;
    public IntRange(int first, int last) {
        this.first = first;
        this.last = last;
    }
    public boolean tryAdvance(IntConsumer action) {
        if (first < last) {
            action.accept(first++);
            return true;
        } else {
            return false;
        }
    }
    public OfInt trySplit() {
        int size = last - first;
        if (size >= 10) {
            int temp = first;
            first += size / 2;
            return new IntRange(temp, first);
        } else {
            return null;
        }
    }
    public long estimateSize() {
        return Math.max(last - first, 0);
    }
    public int characteristics() {
        return ORDERED | DISTINCT | SIZED | NONNULL
            | IMMUTABLE | CONCURRENT | SUBSIZED;
    }
}

回答by Holger

Your mistake is to think that you need an Iteratoror a Collectionto create a Stream. For creating an infinite stream, a single method providing one value after another is enough. So for your class FibonacciSupplierthe simplest use is:

您的错误是认为您需要 anIterator或 aCollection来创建Stream. 对于创建无限流,提供一个又一个值的单一方法就足够了。因此,对于您的课程FibonacciSupplier,最简单的用法是:

IntStream s=IntStream.generate(FibonacciSupplier.infinite()::next);

or, if you prefer boxed values:

或者,如果您更喜欢盒装值:

Stream<Integer> s=Stream.generate(FibonacciSupplier.infinite()::next);

Note that in this case the method does not have to be named nextnor fulfill the Iteratorinterface. But it doesn't matter if it does as with your class. Further, as we just told the stream to use the nextmethod as a Supplier, the hasNextmethod will never be called. It's just infinite.

请注意,在这种情况下,方法不必命名next或实现Iterator接口。但它是否和你的班级一样并不重要。此外,正如我们刚刚告诉流将该next方法用作 a Supplier,该hasNext方法将永远不会被调用。它只是无限的。

Creating a finite stream using your Iteratoris a bit more complicated:

使用您的创建有限流Iterator有点复杂:

Stream<Integer> s=StreamSupport.stream(
  Spliterators.spliteratorUnknownSize(
    FibonacciSupplier.finite(intPredicate), Spliterator.ORDERED),
  false);

In this case if you want a finite IntStreamwith unboxed intvalues your FibonacciSuppliershould implement PrimitiveIterator.OfInt.

在这种情况下,如果您想要一个IntStream带有未装箱int值的有限值,您FibonacciSupplier应该实现PrimitiveIterator.OfInt.

回答by TheCannyCoder

To add another answer, perhaps AbstractSpliterator is a better choice, especially given the example code. Generate is inflexible as there is no [good] way to give a stop condition except by using limit. Limit only accepts a number of items rather than a predicate, so we have to know how many items we want to generate - which might not be possible, and what if the generator is a black box passed to us?

添加另一个答案,也许 AbstractSpliterator 是更好的选择,尤其是考虑到示例代码。Generate 是不灵活的,因为除了使用 limit 之外,没有 [good] 方法来给出停止条件。Limit 只接受多个项目而不是一个谓词,所以我们必须知道我们想要生成多少个项目——这可能是不可能的,如果生成器是一个传递给我们的黑盒怎么办?

AbstractSpliterator is a halfway house between having to write a whole spliterator, and using Iterator/Iterable. AbstractSpliterator lacks just the tryAdvance method where we first check our predicate for being done, and if not pass the generated value to an action. Here's an example of a Fibonacci sequence using AbstractIntSpliterator:

AbstractSpliterator 介于必须编写整个拆分器和使用 Iterator/Iterable 之间。AbstractSpliterator 只缺少 tryAdvance 方法,在该方法中我们首先检查谓词是否已完成,如果未完成,则将生成的值传递给操作。下面是一个使用 AbstractIntSpliterator 的斐波那契数列示例:

public class Fibonacci {
    private static class FibonacciGenerator extends Spliterators.AbstractIntSpliterator
    {
        private IntPredicate hasNextPredicate;
        private int beforePrevious = 0;
        private int previous = 0;

        protected FibonacciGenerator(IntPredicate hasNextPredicate)
        {
            super(Long.MAX_VALUE, 0);
            this.hasNextPredicate = hasNextPredicate;
        }

        @Override
        public boolean tryAdvance(IntConsumer action)
        {
            if (action == null)
            {
                throw new NullPointerException();
            }

            int next = Math.max(1, beforePrevious + previous);
            beforePrevious = previous;
            previous = next;

            if (!hasNextPredicate.test(next))
            {
                return false;
            }

            action.accept(next);

            return true;
        }

        @Override
        public boolean tryAdvance(Consumer<? super Integer> action)
        {
            if (action == null)
            {
                throw new NullPointerException();
            }

            int next = Math.max(1, beforePrevious + previous);
            beforePrevious = previous;
            previous = next;

            if (!hasNextPredicate.test(next))
            {
                return false;
            }

            action.accept(next);

            return true;
        }
    }

    public static void main(String args[])
    {
        Stream<Integer> infiniteStream = StreamSupport.stream(
                new FibonacciGenerator(i -> true), false);

        Stream<Integer> finiteStream = StreamSupport.stream(
                new FibonacciGenerator(i -> i < 100), false);

        // Print with a side-effect for the demo
        infiniteStream.limit(10).forEach(System.out::println);
        finiteStream.forEach(System.out::println);
    }
} 

For more details I've covered generators in Java 8 in my blog http://thecannycoder.wordpress.com/

有关更多详细信息,我已在我的博客http://thecannycoder.wordpress.com/ 中介绍了 Java 8 中的生成器

回答by Arnaud Tournier

You can use the low level stream support primitives and the Spliteratorslibrary to make a stream out of an Iterator.

您可以使用低级流支持原语和Spliterators库从Iterator.

The last parameter to StreamSupport.stream()says that the stream is not parallel. Be sure to let it like that because your Fibonacci iterator depends on previous iterations.

最后一个参数StreamSupport.stream()表示流不是并行的。一定要让它这样,因为你的斐波那契迭代器依赖于之前的迭代。

return StreamSupport.stream( Spliterators.spliteratorUnknownSize( new Iterator<Node>()
{
    @Override
    public boolean hasNext()
    {
        // to implement
        return ...;
    }

    @Override
    public ContentVersion next()
    {
        // to implement
        return ...;
    }
}, Spliterator.ORDERED ), false );