26

看看我所做的以下课程:

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);
    }
} 

以及它的用法:

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;
    }
}

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

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

到目前为止,这纯粹是理论上的,但如果我想首先跳过无限流中的前 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();

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

4

4 回答 4

27

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

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

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

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

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

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

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

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

于 2014-02-24T10:54:56.463 回答
16

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

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

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

Java 8 中还添加了默认方法 Iterable.spliterator,因此每个Iterable类都自动支持Spliterators。实现如下所示:

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

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

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

为了对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;
    }
}
于 2014-02-22T16:19:31.327 回答
0

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

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);
    }
} 

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

于 2014-07-18T21:00:48.910 回答
0

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

最后一个参数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 );
于 2017-05-18T20:20:32.223 回答