I tried following options:
java.lang.reflect.InaccessibleObjectException
)For me, the 1st option seems to be the most problematic, relying on implementation specifics. The 2nd option is not elegant in that it wastes computing time. The 3rd option seems to be the closest to my requirements.
Following I attach my test source-code for anyone running into the same requirements:
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
public class PrngSaveLoadTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, IOException, ClassNotFoundException {
long initialSeed = 4;
final Random prng1 = new Random(initialSeed);
int amountOfSteps = 0;
// Generate arbitrary amount of random numbers
for (int i = 1; i <= 21; i++) {
prng1.nextInt();
amountOfSteps++;
}
// TODO: Save state
// TODO: Load state later, continuing with the same random numbers as if it were the same random number generator
// Option 1: Use reflection to get internal seed of prng1 - does not work, throws exception
//final Random prngRestoredBySeed = new Random(getSeed(prng1));
//System.out.println("Should be identical: " + prng1.nextInt() + " =!= " + prngRestoredBySeed.nextInt());
// Option 2: Progress the second prng instance the same amount of numbers - works
final Random prngRestoredByProgress = new Random(initialSeed);
progressPrng(prngRestoredByProgress, amountOfSteps);
System.out.println("Should be identical: " + prng1.nextInt() + " =!= " + prngRestoredByProgress.nextInt());
// Option 3: Serialize, save, load, deserialize the prng instance
byte[] serializedPrng = serializePrng(prng1);
Random prngRestoredBySerialization = deserializePrng(serializedPrng);
System.out.println("Should be identical: " + prng1.nextInt() + " =!= " + prngRestoredBySerialization.nextInt());
}
/**
* See https://stackoverflow.com/a/29278559/1877010
*/
private static long getSeed(Random prng) throws NoSuchFieldException, IllegalAccessException {
long theSeed;
try {
Field field = Random.class.getDeclaredField("seed");
field.setAccessible(true);
AtomicLong scrambledSeed = (AtomicLong) field.get(prng); //this needs to be XOR'd with 0x5DEECE66DL
theSeed = scrambledSeed.get();
return (theSeed ^ 0x5DEECE66DL);
} catch (Exception e) {
//handle exception
throw e;
}
}
private static void progressPrng(Random prng, long amountOfSteps) {
for (long i = 1; i <= amountOfSteps; i++) {
prng.nextInt();
}
}
private static byte[] serializePrng(Random prng) throws IOException {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(128);
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(prng);
return baos.toByteArray();
}
}
private static Random deserializePrng(byte[] serializedPrng) throws IOException, ClassNotFoundException {
try (ByteArrayInputStream bais = new ByteArrayInputStream(serializedPrng);
ObjectInputStream in = new ObjectInputStream(bais)) {
// Method for deserialization of object
return ((Random) in.readObject());
}
}
}