Created
October 19, 2023 15:54
-
-
Save chrisruffalo/ede61c2c781a742cf7ec47c7084d9a16 to your computer and use it in GitHub Desktop.
Random Iterator in Java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package io.github.chrisruffalo.pintle.resolution.resolver; | |
import java.util.*; | |
/** | |
* Randomly iterates through a list. This class is not thread | |
* safe. This is a lot more efficient than the following: | |
* <pre> | |
* final List<T> copy = new ArrayList(source); | |
* Collections.shuffle(copy); | |
* Iterator<T> iterator = copy.iterator(); | |
* </pre> | |
* <br/><br/> | |
* This is the test case that was used to verify this somewhat works: | |
* <pre> | |
* @Test | |
* public void random() { | |
* final List<String> ordered = new ArrayList<>(); | |
* for (int i = 0; i <= 5000; i++) { | |
* ordered.add(String.format("%d", i)); | |
* } | |
* // there are now 5000! combinations of this, this should | |
* // _never_ collide | |
* final String joined = Strings.join("", ordered); | |
* final Iterator<String> random = new RandomIterator<>(ordered); | |
* final StringBuilder randomJoined = new StringBuilder(); | |
* while(random.hasNext()) { | |
* randomJoined.append(random.next()); | |
* } | |
* Assertions.assertEquals(joined.length(), randomJoined.length()); | |
* Assertions.assertNotEquals(joined,randomJoined); | |
* } | |
* </pre> | |
* | |
* @param <T> of the list type. | |
*/ | |
public class RandomIterator<T> implements Iterator<T> { | |
/** | |
* If you want to use this for anything secure or | |
* that matters you'd need to use a SecureRandom | |
* but don't do that. | |
*/ | |
private final Random random = new Random(); | |
/** | |
* The source content list. This list is not modified. | |
*/ | |
private final List<T> contents; | |
/** | |
* A bit set that represents the iterated elements | |
* in the list. | |
*/ | |
private final BitSet visited; | |
/** | |
* Build a new iterator by starting from a list | |
* of contents. Only a list will work because | |
* of the ability to access nodes by index. | |
* | |
* @param contents that need to be randomly iterated over | |
*/ | |
public RandomIterator(List<T> contents) { | |
this.contents = contents; | |
visited = new BitSet(contents.size()); | |
} | |
@Override | |
public boolean hasNext() { | |
return visited.nextClearBit(0) < contents.size(); | |
} | |
@Override | |
public T next() { | |
// this uses the hasNext function to decide if there | |
// are remaining un-iterated elements | |
if (hasNext()) { | |
// this loop continuously | |
int index; | |
// using the index of the first clear bit allows | |
// for a little smaller range in the random generator | |
// and makes it so that longer lists are handled | |
// a little more efficiently | |
int first = visited.nextClearBit(0); | |
do { | |
index = random.nextInt(contents.size() - first) + first; | |
} while(visited.get(index)); | |
// a new element can be retrieved but first we mark it as iterated in the bitset | |
visited.set(index); | |
// and we get the contents of that element from the list | |
return contents.get(index); | |
} | |
throw new NoSuchElementException("No elements remain"); | |
} | |
@Override | |
public void remove() { | |
throw new UnsupportedOperationException("The 'remove' operation is not supported"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment