在我的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()
方法实际上是如何工作的,因为它似乎并不是随机的。
感谢。
答案 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();
}