Seems a very basic question. I\'ve an ArrayList
and I would like to iterate over it. Normally,
for(int i : al) {
// some c
public class RandomIterator<T> implements Iterator<T> {
private static final SecureRandom RANDOM = new SecureRandom();
private final int[] order;
private final List<T> elements;
public RandomIterator(List<T> elements) {
this.elements = elements;
this.order = generateRandomOrder(elements.size());
}
private int[] generateRandomOrder(int size) {
int[] index = new int[size];
for (int i = 0; i < size; i++) {
index[i] = i;
}
int swap;
for (int i = 0; i < size; i++) {
int randomIndex = getRandomInt(0, size);
swap = index[i];
index[i] = index[randomIndex];
index[randomIndex] = swap;
}
return index;
}
private int currentIndex = 0;
private int getRandomInt(int lowerBound, int upperBound) {
return RANDOM.nextInt(upperBound - lowerBound) + lowerBound;
}
@Override
public boolean hasNext() {
return currentIndex != elements.size();
}
@Override
public T next() {
return elements.get(order[currentIndex++]);
}
}
This only works if you guarantee that the elements in the list are placed in positions exactly between indexes [0,size).
You can use the normal Random if you are concerned about the performance penalty of SecureRandom (I prefer secure, yeah 8 times slower than normal random but secure!).
Use the following class:
import java.util.Enumeration;
import java.util.Random;
public class RandomPermuteIterator implements Enumeration<Long> {
int c = 1013904223, a = 1664525;
long seed, N, m, next;
boolean hasNext = true;
public RandomPermuteIterator(long N) throws Exception {
if (N <= 0 || N > Math.pow(2, 62)) throw new Exception("Unsupported size: " + N);
this.N = N;
m = (long) Math.pow(2, Math.ceil(Math.log(N) / Math.log(2)));
next = seed = new Random().nextInt((int) Math.min(N, Integer.MAX_VALUE));
}
public static void main(String[] args) throws Exception {
RandomPermuteIterator r = new RandomPermuteIterator(100);
while (r.hasMoreElements()) System.out.print(r.nextElement() + " ");
//output:50 52 3 6 45 40 26 49 92 11 80 2 4 19 86 61 65 44 27 62 5 32 82 9 84 35 38 77 72 7 ...
}
@Override
public boolean hasMoreElements() {
return hasNext;
}
@Override
public Long nextElement() {
next = (a * next + c) % m;
while (next >= N) next = (a * next + c) % m;
if (next == seed) hasNext = false;
return next;
}
}
How about this way (more functional); it even includes a main() to demonstrate. Basically, generate random numbers in the range of your list size until you have 1 of them all. We use HashSet to take care of duplicate random numbers in our range. Then, delegate iteration to the iterator of your index hashset. Logic of "hasNext()" becomes trivial through delegate.
public class RandomIterator<T> implements Iterator<T> {
private Iterator<Integer> indicies;
List<T> delegate;
public static void main(String[] args) {
Random r = new Random();
List<Integer> numbers = IntStream.generate(r::nextInt).limit(10).boxed().collect(toCollection(ArrayList::new));
List<Integer> results = new ArrayList<>();
for(RandomIterator<Integer> test = new RandomIterator<>(numbers); test.hasNext(); ) {
results.add(test.next());
}
System.out.println("In list: " + numbers);
System.out.println("random iteration " + results);
if(results.containsAll(numbers) && results.size() == numbers.size())
System.out.println("Everything accounted for");
else
System.out.println("Something broke!");
}
public RandomIterator(List<T> delegate) {
Random r = new Random();
this.delegate = delegate;
Set<Integer> indexSet = new LinkedHashSet<>();
while(indexSet.size() != delegate.size())
indexSet.add(r.nextInt(delegate.size()));
System.out.println(indexSet);
indicies = indexSet.iterator();
}
@Override
public boolean hasNext() {
return indicies.hasNext();
}
@Override
public T next() {
return delegate.get(indicies.next());
}
}
If you just want an print 100 random numbers:
IntStream.generate(r::nextInt).limit(100).forEach(System.out::println);
if you want an iterator (for whatever reason:)
IntStream.generate(r::nextInt).limit(100).boxed().collect(Collectors.toList()).iterator();
Here is a shuffling iterator that is very space&time-efficient when requesting only a few elements. It's very reasonable in all other cases too; runtime and space are both O(n). The only dynamic structure is a HashMap with up to half the size of the input list (although it will be much smaller near the beginning and the end) and Integer keys.
static <A> Iterator<A> shuffledIterator(final List<A> l) {
return new Iterator<A>() {
Random randomizer = new Random();
int i = 0, n = l.size();
HashMap<Integer, A> shuffled = new HashMap();
public boolean hasNext() { return i < n; }
public A next() {
int j = i+randomizer.nextInt(n-i);
A a = get(i), b = get(j);
shuffled.put(j, a);
shuffled.remove(i);
++i;
return b;
}
A get(int i) {
return shuffled.containsKey(i) ? shuffled.get(i) : l.get(i);
}
};
}
You can use Collections.shuffle() on the list.
Note that this will shuffle the list itself, so if order is important you should make a copy of it (and shuffle the copy).
List<Customer> newList = new ArrayList<>( oldList ) ;
Collections.shuffle( newList ) ;
Alternatively you could create a random array which has elements 0 - List.size()-1
and using those as indices to access "random" elements in the List
.