Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 628
Patrick Grimard Avatar asked Sep 18 '14 12:09

Patrick Grimard


People also ask

Which is better findFirst or findAny?

When Stream is unordered, findFirst() and findAny() are the same. But when Stream is ordered, findAny() will be better.

What is the difference between findFirst () and findAny ()?

The findAny() method returns any element from a Stream, while the findFirst() method returns the first element in a Stream.

How does stream findAny work?

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.

What is findAny in Java?

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.


2 Answers

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.)

like image 134
Keppil Avatar answered Sep 20 '22 17:09

Keppil


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();
}
like image 26
Holger Avatar answered Sep 21 '22 17:09

Holger