我可以检查Java 8流是否包含至少n个元素吗?

2022-09-04 23:26:03

我有一个Java 8流,我想从中(统一)随机选择一个元素。该流可以包含从零到数万个元素的任何地方。

我已经实现了一种算法,该算法使用类似MapReduce的模式选择一个,但是对于非常小的流,将项目收集到List中并返回具有随机索引的项目可能会更有效。然而,为此,我必须数一数。流确实有一个count()方法,但它将它们全部计算在内,我对实际计数并不感兴趣,我所关心的只是它是否包含多个待定数字。有谁知道是否存在这种方法?我找不到它,但可能有一些我忽略了的东西,或者一些聪明的技巧来找到它。

P.S.:我知道有时没有必要优化代码。但我想尝试一下,只是为了体验。我是学生。

P.P.S.:我已经在这里复制了我的算法,以防有人感兴趣(或者想寻找错误,我还没有测试过;-)

stream
    .parallel()
    .map(t -> new Pair<T, Integer>(t, 1))
    .reduce((Pair<T, Integer> t, Pair<T, Integer> u) -> {
        if (rand.nextDouble() <= (t.getValue1() / (double) (t.getValue1() + u.getValue1()))) {
            return new Pair<>(t.getValue0(), t.getValue1() + u.getValue1());
        } else {
            return new Pair<>(u.getValue0(), t.getValue1() + u.getValue1());
        }
    })
    .map(t -> t.getValue0());

(这些对来自 org.javatuples,现在 Java 支持类似函数式编程的接口,缺少元组确实变得有点痛苦)。


答案 1

您的代码不会从均匀分布返回元素。这取决于顺序,流提供元素来减少方法。在一般情况下,您不能认为订单不会是特殊的订单。解决你的任务:如果你有足够的内存,可以编写RandomComparator(将以前的结果保存在Map中),用这个比较器对你的流进行排序,并得到第一个元素(不要使用findAny)。如果流太大,可以使用 RandomFilter 对其进行采样。

顺便说一句,如果您的流中有SIZE标志,则任务是微不足道的。只需获取大小,生成随机索引并使 spip :)


答案 2

我相信,最初的问题已经得到了回答,但是我在谷歌搜索“java流至少n个元素”或类似内容时一直在这里着陆,所以也许这对某些人仍然有帮助。

对我有帮助的是方法。我们将其设置为预期的最小值,然后对所有元素进行计数。一旦达到限制,这将停止计数。下面是一个完整的示例:limit()

class Scratch
{
    public static void main(String[] args)
    {
        List<Integer> list1 = Arrays.asList(1, 2, 3);
        List<Integer> list2 = Arrays.asList(1, 2, 3, 4);

        System.out.println(streamContainsAtLeastNElements(list1.stream(), 4));
        // --> false
        System.out.println(streamContainsAtLeastNElements(list2.stream(), 4));
        // --> true
    }

    public static boolean streamContainsAtLeastNElements(Stream<?> stream, long minCount)
    {
        return stream.limit(minCount).count() == minCount;
    }
}

请注意,它将消耗您的流。此外,如果您的流实现了一些复杂的排序例程,它可能仍然很慢。在这种情况下,请考虑添加 。unordered()