微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Java Streams – 过滤先前过滤的值

我正在尝试使用 Java的Streams并试图找出可能的内容以及它们的优点和缺点.目前我正在尝试使用流来实现Eratosthenes的Sieve,但似乎无法找到循环使用先前过滤的值而不将其存储在单独集合中的好方法.

我想要完成这样的事情:

IntStream myStream = IntStream.range(0,3);
myStream.filter(s -> {
    System.out.print("[filtering "+s+"] ");
    myStream.forEach(q -> System.out.print(q+","));
    System.out.println();
    return true; //eventually respond to values observed on the line above
});

具有所需的输出

[filtering 0] 
[filtering 1] 0,[filtering 2] 0,1,[filtering 3] 0,2,

请注意,在过滤每个新值时,会观察到所有先前过滤的值.这样可以轻松实现Eratosthenes的Sieve,因为我可以过滤掉所有非素数值,并为每个新值检查对所有先前通过素数过滤器的数字的可除性.

但是,上面的示例在NetBeans中给出了一个错误

local variables referenced from a lambda expression must be final or effectively final

这似乎是因为我在已经作用于myStream的过滤器中引用myStream.是否有任何解决错误的好方法(即,制作仅包含到目前为止已过滤的值的流的最终副本),或者是否有更好的方法解决此类问题而不使用单独的集合来存储值?

解决方法

我设法使用Eratosthenes的Sieve创建了一个无限的素数流,但它实际上并没有使用过去的值.相反,它删除了尾部中的素数的倍数(以懒惰的方式,因为尾部是无限的),就像原始的Eratosthenes算法的Sieve一样.为此,我使用Iterator作为辅助(因为Stream只能使用一次)并为流实现了lazyConcat.
class StreamUtils {
    public static IntStream fromIterator(PrimitiveIterator.OfInt it) {
        return StreamSupport.intStream(
                Spliterators.spliteratorUnkNownSize(it,Spliterator.ORDERED),false);
    }

    public static IntStream lazyConcat(supplier<IntStream> a,supplier<IntStream> b) {
        return StreamSupport.intStream(new Spliterator.OfInt() {
            boolean beforeSplit = true;
            Spliterator.OfInt spliterator;

            @Override
            public OfInt trySplit() {
                return null;
            }

            @Override
            public long estimateSize() {
                return Long.MAX_VALUE;
            }

            @Override
            public int characteristics() {
                return Spliterator.ORDERED;
            }

            @Override
            public boolean tryAdvance(IntConsumer action) {
                boolean hasNext;
                if (spliterator == null) {
                    spliterator = a.get().spliterator();
                }
                hasNext = spliterator.tryAdvance(action);
                if (!hasNext && beforeSplit) {
                    beforeSplit = false;
                    spliterator = b.get().spliterator();
                    hasNext = spliterator.tryAdvance(action);
                }
                return hasNext;
            }
        },false);
    }
}

我的Eratosthenes流筛选器看起来像这样:

class Primes {
    public static IntStream stream() {
        return sieve(IntStream.iterate(2,n -> n + 1));
    }

    private static IntStream sieve(IntStream s) {
        PrimitiveIterator.OfInt it = s.iterator();
        int head = it.nextInt();
        IntStream tail = StreamUtils.fromIterator(it);
        return StreamUtils.lazyConcat(
                () -> IntStream.of(head),() -> sieve(tail.filter(n -> n % head != 0)));
    }
}

然后我们可以这样使用它:

System.out.println(Primes.stream().limit(20).Boxed().collect(Collectors.toList()));

输出

[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71]

我认为这是一个很好的练习,但它似乎效率很低,而且根本不适合堆栈.

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐