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.
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.
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
.
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.
Thanks.
When Stream is unordered, findFirst() and findAny() are the same. But when Stream is ordered, findAny() will be better.
The findAny() method returns any element from a Stream, while the findFirst() method returns the first element in a Stream.
Note : findAny() is a terminal-short-circuiting operation of Stream interface. This method returns any first element satisfying the intermediate operations. This is a short-circuit operation because it just needs 'any' first element to be returned and terminate the rest of the iteration.
The findAny() method of the Java Stream returns an Optional for some element of the stream or an empty Optional if the stream is empty. Here, Optional is a container object which may or may not contain a non-null value.
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.
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.
This is what the Javadoc says on the subject:
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.)
Don’t collect into a List
when all you want is a single item. Just pick one item from the stream. By picking the item via Stream
operations you can even handle counts bigger than Integer.MAX_VALUE
and 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).
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();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With