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
ncalls returns unique elements.Use
Collections#shuffle():List<Foo> list = createItSomehow(); Collections.shuffle(list); Foo foo = list.get(0);It enables you to get
nunique 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.
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
ncalls returns unique elements.Use
Collections#shuffle():List<Foo> list = createItSomehow(); Collections.shuffle(list); Foo foo = list.get(0);It enables you to get
nunique 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());
}
anyItem is a method and the System.out.println call is after your return statement so that won't compile anyway since it is unreachable.
Might want to re-write it like:
import java.util.ArrayList;
import java.util.Random;
public class Catalogue
{
private Random randomGenerator;
private ArrayList<Item> catalogue;
public Catalogue()
{
catalogue = new ArrayList<Item>();
randomGenerator = new Random();
}
public Item anyItem()
{
int index = randomGenerator.nextInt(catalogue.size());
Item item = catalogue.get(index);
System.out.println("Managers choice this week" + item + "our recommendation to you");
return item;
}
}
public static Item getRandomChestItem(List<Item> items) {
return items.get(new Random().nextInt(items.size()));
}
Is there a smart way to get UNIQUE random elements from a collection in Java?
java - Select N random elements from a List efficiently (without toArray and change the list) - Stack Overflow
Pick multiple random elements from a list in Java - Stack Overflow
java - How can I select random element from the list? - Stack Overflow
Videos
Right now I'm using the Random class, which .nextInt(upperbound) provides me with random integers that can be used to index an ArrayList object. I want to get several random elements and want them to be unique, however.
I can come up with some solutions, but none of them are very elegant. Any ideas?
Edit: current solution is to remove the randomly selected element, but I also have to decrease the upperbound (myList.size() - i ) on each iteration of the for-loop.
I know that Pyhton has a function that picks n unique random elements from a list, is there anything similar in Java?
You are probably looking for something like Resorvoir Sampling.
Start with an initial array with first k elements, and modify it with new elements with decreasing probabilities:
java like pseudo code:
E[] r = new E[k]; //not really, cannot create an array of generic type, but just pseudo code
int i = 0;
for (E e : list) {
//assign first k elements:
if (i < k) { r[i++] = e; continue; }
//add current element with decreasing probability:
j = random(i++) + 1; //a number from 1 to i inclusive
if (j <= k) r[j] = e;
}
return r;
This requires a single pass on the data, with very cheap ops every iteration, and the space consumption is linear with the required output size.
If n is very small compared to the length of the list, take an empty set of ints and keep adding a random index until the set has the right size.
If n is comparable to the length of the list, do the same, but then return items in the list that don't have indexes in the set.
In the middle ground, you can iterate through the list, and randomly select items based on how many items you've seen, and how many items you've already returned. In pseudo-code, if you want k items from N:
for i = 0 to N-1
if random(N-i) < k
add item[i] to the result
k -= 1
end
end
Here random(x) returns a random number between 0 (inclusive) and x (exclusive).
This produces a uniformly random sample of k elements. You could also consider making an iterator to avoid building the results list to save memory, assuming the list is unchanged as you're iterating over it.
By profiling, you can determine the transition point where it makes sense to switch from the naive set-building method to the iteration method.
Try this:
public static List<String> pickNRandom(List<String> lst, int n) {
List<String> copy = new ArrayList<String>(lst);
Collections.shuffle(copy);
return n > copy.size() ? copy.subList(0, copy.size()) : copy.subList(0, n);
}
I'm assuming that there are no repeated elements in the input list, also I take the precaution of shuffling a copy for leaving the original list undisturbed. Use it like this:
List<String> randomPicks = pickNRandom(teamList, 3);
The shuffle approach is the most idiomatic: after that, first K elements are exactly what you need.
If K is much less than the length of the list, you may want to be faster. In this case, iterate through the list, randomly exchanging the current element with itself or any of the elements after it. After the K-th element, stop and return the K-prefix: it will be already perfectly shuffled, and you don't need to care about the rest of the list.
(obviously, you'd like to use ArrayList here)
int len = list.size();
Random randomGenerator = new Random();
int randomInt = randomGenerator.nextInt(len);
If you only need one element you can use the Random class to generate a (pseudo) random value (as you wrote in your question):
E element = list.get(new Random().nextInt(list.size()));
Remember LinkedList.get(index) is an O(n) operation, as noted in the comments it's better to use an ArrayList for this purpose.
If you want to shuffle the whole array you can use the Collections api like this:
Collections.shuffle(list);