Java 8 Stream.findAny() 与在流中查找随机元素

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25912185/
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-11-02 08:56:17  来源:igfitidea点击:

Java 8 Stream.findAny() vs finding a random element in the stream

javajava-8java-stream

提问by Patrick Grimard

In my Spring application, I have a Couchbase repository for a document type of QuoteOfTheDay. The document is very basic, just has an id field of type UUID, value field of type String and created date field of type Date.

在我的 Spring 应用程序中,我有一个 Couchbase 存储库,用于QuoteOfTheDay. 该文档非常基础,只有一个 UUID 类型的 id 字段、String 类型的 value 字段和 Date 类型的创建日期字段。

In my service class, I have a method that returns a random quote of the day. Initially I tried simply doing the following, which returned an argument of type Optional<QuoteOfTheDay>, but it would seem that findAny() would pretty much always return the same element in the stream. There's only about 10 elements at the moment.

在我的服务类中,我有一个方法可以返回当天的随机报价。最初我尝试简单地执行以下操作,它返回一个 type 参数Optional<QuoteOfTheDay>,但似乎 findAny() 几乎总是返回流中的相同元素。目前只有大约 10 个元素。

public Optional<QuoteOfTheDay> random() {
    return StreamSupport.stream(repository.findAll().spliterator(), false).findAny();
}

Since I wanted something more random, I implemented the following which just returns a QuoteOfTheDay.

由于我想要更随机的东西,我实现了以下只返回一个QuoteOfTheDay.

public QuoteOfTheDay random() {
    int count = Long.valueOf(repository.count()).intValue();

    if(count > 0) {
        Random r = new Random();

        List<QuoteOfTheDay> quotes = StreamSupport.stream(repository.findAll().spliterator(), false)
                .collect(toList());

        return quotes.get(r.nextInt(count));
    } else {
        throw new IllegalStateException("No quotes found.");
    }
}

I'm just curious how the findAny()method of Stream actually works since it doesn't seem to be random.

我只是好奇findAny()Stream的方法实际上是如何工作的,因为它似乎不是随机的。

Thanks.

谢谢。

回答by Keppil

The reason behind findAny()is to give a more flexible alternative to findFirst(). If you are not interested in getting a specific element, this gives the implementing stream more flexibility in case it is a parallel stream.

背后的原因findAny()是为findFirst(). 如果您对获取特定元素不感兴趣,这会为实现流提供更大的灵活性,以防它是并行流。

No effort will be made to randomize the element returned, it just doesn't give the same guarantees as findFirst(), and might therefore be faster.

不会对返回的元素进行随机化,它只是不提供与 相同的保证findFirst(),因此可能会更快。

This is what the Javadocsays on the subject:

这是Javadoc在这个主题上所说的:

The behavior of this operation is explicitly nondeterministic; it is free to select any element in the stream. This is to allow for maximal performance in parallel operations; the cost is that multiple invocations on the same source may not return the same result. (If a stable result is desired, use findFirst() instead.)

此操作的行为明显是不确定的;可以自由选择流中的任何元素。这是为了在并行操作中实现最大性能;代价是对同一源的多次调用可能不会返回相同的结果。(如果需要稳定的结果,请改用 findFirst()。)

回答by Holger

Don't collect into a Listwhen all you want is a single item. Just pick one item from the stream. By picking the item via Streamoperations you can even handle counts bigger than Integer.MAX_VALUEand don't need the “interesting” way of hiding the fact that you are casting a long to an int(that?Long.valueOf(repository.count()).intValue()thing).

List当你想要的只是一个项目时,不要收集到一个。只需从流中选择一项。通过Stream操作选择项目,您甚至可以处理大于Integer.MAX_VALUE并且不需要“有趣”的方式来隐藏您正在向int(那个?Long.valueOf(repository.count()).intValue()东西)投射 long 的事实。

public Optional<QuoteOfTheDay> random() {
    long count = repository.count();
    if(count==0) return Optional.empty();
    Random r = new Random();
    long randomIndex=count<=Integer.MAX_VALUE? r.nextInt((int)count):
        r.longs(1, 0, count).findFirst().orElseThrow(AssertionError::new);
    return StreamSupport.stream(repository.findAll().spliterator(), false)
        .skip(randomIndex).findFirst();
}