Take n random elements from a List<E>?
Two main ways.
Use
Random#nextInt(int)
:List<Foo> list = createItSomehow(); Random random = new Random(); Foo foo = list.get(random.nextInt(list.size()));
It's however not guaranteed that successive
n
calls returns unique elements.Use
Collections#shuffle()
:List<Foo> list = createItSomehow(); Collections.shuffle(list); Foo foo = list.get(0);
It enables you to get
n
unique elements by an incremented index (assuming that the list itself contains unique elements).
In case you're wondering if there's a Java 8 Stream approach; no, there isn't a built-in one. There's no such thing as Comparator#randomOrder()
in standard API (yet?). You could try something like below while still satisfying the strict Comparator
contract (although the distribution is pretty terrible):
List<Foo> list = createItSomehow();
int random = new Random().nextInt();
Foo foo = list.stream().sorted(Comparator.comparingInt(o -> System.identityHashCode(o) ^ random)).findFirst().get();
Better use Collections#shuffle()
instead.
Most of the proposed solutions till now suggest either a full list shuffle or successive random picking by checking uniqueness and retry if required.
But, we can take advantage of the Durstenfeld's algorithm (the most popular Fisher-Yates variant in our days).
Durstenfeld's solution is to move the "struck" numbers to the end of the list by swapping them with the last unstruck number at each iteration.
Due to the above, we don't need to shuffle the whole list, but run the loop for as many steps as the number of elements required to return. The algorithm ensures that the last N elements at the end of the list are 100% random if we used a perfect random function.
Among the many real-world scenarios where we need to pick a predetermined (max) amount of random elements from arrays/lists, this optimized method is very useful for various card games, such as Texas Poker, where you a-priori know the number of cards to be used per game; only a limited number of cards is usually required from the deck.
public static <E> List<E> pickNRandomElements(List<E> list, int n, Random r) {
int length = list.size();
if (length < n) return null;
//We don't need to shuffle the whole list
for (int i = length - 1; i >= length - n; --i)
{
Collections.swap(list, i , r.nextInt(i + 1));
}
return list.subList(length - n, length);
}
public static <E> List<E> pickNRandomElements(List<E> list, int n) {
return pickNRandomElements(list, n, ThreadLocalRandom.current());
}
If you want to successively pick n elements from the list and be able to do so without replacement over and over and over again, you are probably best of randomly permuting the elements, then taking chunks off in blocks of n. If you randomly permute the list you guarantee statistical randomness for each block you pick out. Perhaps the easiest way to do this would be to use Collections.shuffle
.