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

时间:2014-09-18 12:07:17

标签: java java-8 java-stream

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

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

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

由于我想要更随机的东西,我实现了以下只返回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.");
    }
}

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

感谢。

2 个答案:

答案 0 :(得分:23)

findAny()背后的原因是为findFirst()提供了更灵活的替代方案。如果您对获取特定元素不感兴趣,那么在实现流是并行流的情况下,这将为实现流提供更大的灵活性。

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

这是Javadoc关于这个问题的说法:

  

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

答案 1 :(得分:11)

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

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