package freenet.support; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Random; /** * Reusable read-only iterator with randomized array iteration order. * * This iterator iterates over the underlying array, yielding its elements in an (optionally) * randomized order. Instances can be reused by {@link #reset(Random) resetting} them to their * original position, optionally yielding a new random permutation of the array elements. * * Storage requirements are linear in the size of the underlying array, all instance operations * finish in constant time. * * Instances of this class are not thread-safe. * * @author bertm */ public class RandomArrayIterator<E> implements Iterator<E> { /** The underlying array. */ private final E[] array; /** Permutation state. This array contains a permutation of indices into {@link #array}. */ private final int[] indices; /** Random source for the current run. */ private Random random; /** Current position in indices array. */ private int i; /** * Creates a new randomized iterator for the given array. * @param array The underlying array * @param random Random source for the iteration order */ public RandomArrayIterator(E[] array, Random random) { this.array = array; this.indices = sequence(array.length); reset(random); } /** * Creates a new iterator for the given array. Initially, the array is iterated in the original * ordering, {@link #reset(Random)} the iterator to get a new random iteration ordering. * @param array The underlying array */ public RandomArrayIterator(E[] array) { this(array, null); } /** * Resets this iterator. If a random source is given, the next sequence of calls to * {@link #next()} will iterate over the array in a new random order. If it is {@code null}, * the iteration order remains unaltered. * @param random Random source for the next run, or {@code null} to repeat the previous run */ public void reset(Random random) { this.random = random; i = 0; } @Override public boolean hasNext() { return i < indices.length; } @Override public E next() { if (!hasNext()) { throw new NoSuchElementException(); } if (random != null) { shuffleStep(); } return array[indices[i++]]; } @Override public void remove() { throw new UnsupportedOperationException(); } /** * Creates an integer sequence array. * @param length The length of the resulting array. * @return an array holding values [0, 1, ..., length - 1] */ private int[] sequence(int length) { final int[] ret = new int[length]; for (int i = 0; i < length; i++) { ret[i] = i; } return ret; } /** * Perfoms a Fisher–Yates shuffle step from the current position. */ private void shuffleStep() { // Swap the index at position i with a random subsequent index. final int j = random.nextInt(indices.length - i) + i; final int tmp = indices[j]; indices[j] = indices[i]; indices[i] = tmp; } }