Reputation: 3338
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
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
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
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
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
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
Reputation: 3747
private Object getRandomObject(Collection from) {
Random rnd = new Random();
int i = rnd.nextInt(from.size());
return from.toArray()[i];
}
Upvotes: 8
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
Reputation: 32831
Several options (by order of efficiency):
Upvotes: 1
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