Secret
Secret

Reputation: 3338

Get Random Element from Collection

I have a Collection<Obj> how do I get a random Obj from it?

I've checked the docs and there doesn't seem to be a way, since iterator is the only way to access the collection. Do I have to iterate over it to get a random object!?

Upvotes: 21

Views: 41541

Answers (9)

user14647130
user14647130

Reputation:

I know this is an old thread, but I'm surprised that nobody mentioned the RandomAccess interface which marks a List implementation as having a very fast access by index.

Here is RandomAccess's documentation:

Marker interface used by List implementations to indicate that they support fast (generally constant time) random access. The primary purpose of this interface is to allow generic algorithms to alter their behavior to provide good performance when applied to either random or sequential access lists.

For example: It is implemented by ArrayList in contrast to LinkedList.

This is my solution that takes advantage of it:

public static <E> E getRandomElement(Collection<E> collection) 
{
    if(collection.isEmpty()) 
        throw new IllegalArgumentException("Cannot return a random value from an empty collection!");

    int randomIndex = ThreadLocalRandom.current().nextInt(collection.size());

    if(collection instanceof RandomAccess) 
        return ((List<E>) collection).get(randomIndex);

    for(E element : collection)
    {
        if(randomIndex == 0)
            return element;

        randomIndex--;
    }

    throw new IllegalStateException("How did we get here?"); //unreachable
}

Upvotes: 5

Unmitigated
Unmitigated

Reputation: 89204

One can use Stream#skip along with ThreadLocalRandom.

public static <T> T getRandomElement(final Collection<T> collection) {
    return Objects.requireNonNull(collection, "collection is null").stream()
      .skip(ThreadLocalRandom.current().nextInt(Math.max(collection.size(), 1)))
      .findFirst().orElseThrow(() -> new IllegalArgumentException("collection is empty"));
}

Upvotes: 0

Ernest Sadykov
Ernest Sadykov

Reputation: 831

Solution using Google Guava Iterables.get() method:

private <T> T getRandomObject(Collection<T> from) {
   Random rnd = new Random();
   int i = rnd.nextInt(from.size());
   return Iterables.get(from, i);
}

If you want to handle empty collections as well, the method with defaultValue may be used: Iterables.get(from, i, null)

Upvotes: 0

RKumsher
RKumsher

Reputation: 2897

If you don't mind a 3rd party library, the Utils library has a IterableUtils that has a randomFrom(Iterable iterable) method that will take a Collection and return a random element from it

Collection<Object> collection = ....;
Object random = IterableUtils.randomFrom(collection);

It is in the Maven Central Repository at:

<dependency>
  <groupId>com.github.rkumsher</groupId>
  <artifactId>utils</artifactId>
  <version>1.3</version>
</dependency>

Upvotes: 1

Witold Kaczurba
Witold Kaczurba

Reputation: 10485

Using Lambdas you can do this quite quickly and handle the case when Collection is empty.

public static <E> Optional<E> getRandom (Collection<E> e) {

    return e.stream()
            .skip((int) (e.size() * Math.random()))
            .findFirst();
}

Upvotes: 58

Autocrab
Autocrab

Reputation: 3747

private Object getRandomObject(Collection from) {
   Random rnd = new Random();
   int i = rnd.nextInt(from.size());
   return from.toArray()[i];
}

Upvotes: 8

Peter Lawrey
Peter Lawrey

Reputation: 533472

The most efficient it to only iterate as far as you need.

public static <T> T random(Collection<T> coll) {
    int num = (int) (Math.random() * coll.size());
    for(T t: coll) if (--num < 0) return t;
    throw new AssertionError();
}

Upvotes: 30

Maurice Perry
Maurice Perry

Reputation: 32831

Several options (by order of efficiency):

  • use a List instead of a Collection,
  • generate a random index with random.nextInt(collection.size()), get an iterator and iterate,
  • generate a random index with random.nextInt(collection.size()), convert the collection into an array with toArray(), and index that array.

Upvotes: 1

A Paul
A Paul

Reputation: 8251

User Collections.shuffle(list);. Then you can just get the first element. It will be random.

or you can also do this

int size = list.size();
int item = new Random().nextInt(size); 
list.get(item )

Upvotes: -4

Related Questions