Java 17 Random Number Generators: RandomGenerator API
Introduction
Before Java 17, Java had several random number generator classes -
OUTPUT of the above code
The new algorithms
Java 17 added a family of generators called LXM - each combines a Linear congruential generator with an Xor-based generator and a Mixing function - plus two popular Xoroshiro/Xoshiro algorithms. Their names describe their structure:
OUTPUT of the above code
The legacy classes appear too, so a factory lookup can return any generator. More state bits mean a longer period and better behaviour when many generators are used at once, at the cost of a little more memory.
Choosing an algorithm and seeding it
A generator is created by name with
OUTPUT of the above code
Generators for parallel work
Using one generator from many threads is slow (because of contention) or incorrect, and giving each thread a generator with a random seed risks overlapping sequences. The new API offers two safe ways to make many independent generators -
OUTPUT of the above code
java.util.Random, ThreadLocalRandom, SplittableRandom and SecureRandom - with no common interface, so code could not switch between them, and no way to use the newer, better algorithms developed since Random was designed in 1995. Java 17Click to read tutorial: Overview of New Features in Java 17 (JEP 356) introduced enhanced pseudo-random number generators: a common RandomGenerator interface, a family of modern algorithms, and a factory for choosing one by name or by its properties. This tutorial explains the RandomGenerator API with examples - using the interface, choosing an algorithm, getting reproducible sequences from a seed, and generators designed for parallel work.
The RandomGenerator interface
java.util.random.RandomGenerator declares the methods all generators share: nextInt(), nextInt(bound), the new nextInt(origin, bound), nextDouble(), nextGaussian(), nextBoolean(), and streams such as ints() and doubles(). All the existing classes now implement it, so code can be written against the interface and given any generator -
Writing code against RandomGenerator
//RandomGeneratorDemo.java
import java.security.SecureRandom;
import java.util.Random;
import java.util.SplittableRandom;
import java.util.random.RandomGenerator;
public class RandomGeneratorDemo {
// Works with any generator: old or new, seeded or secure
static String rollDice(RandomGenerator rng, int count) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < count; i++) sb.append(rng.nextInt(1, 7)).append(' '); // nextInt(origin, bound) is new
return sb.toString().trim();
}
public static void main(String[] args) {
// Seeded generators give the same rolls on every run
System.out.println("Random(42) : " + rollDice(new Random(42), 8));
System.out.println("SplittableRandom(42): " + rollDice(new SplittableRandom(42), 8));
// Unseeded ones differ every run, but work through the same interface
for (RandomGenerator rng : new RandomGenerator[] {new SecureRandom(), RandomGenerator.getDefault()}) {
System.out.println(rng.getClass().getSimpleName() + " rolled " + rollDice(rng, 8).split(" ").length + " dice");
}
System.out.println("Default algorithm: " + RandomGenerator.getDefault().getClass().getSimpleName());
}
}
Random(42) : 3 4 1 3 1 2 6 3 SplittableRandom(42): 6 1 4 1 6 6 3 6 SecureRandom rolled 8 dice L32X64MixRandom rolled 8 dice Default algorithm: L32X64MixRandom
RandomGenerator.getDefault() returns a generator using a good general-purpose algorithm chosen by the JDK - currently L32X64MixRandom. Use it when you do not have a specific reason to pick an algorithm.
L64X128MixRandom combines a 64-bit LCG with a 128-bit xor-based generator. They are fast, have very long periods, and pass the statistical tests which java.util.Random's old 48-bit LCG fails. RandomGeneratorFactory lists every available algorithm with its properties -
Listing the available algorithms
//ListAlgorithms.java
import java.util.Comparator;
import java.util.random.RandomGeneratorFactory;
public class ListAlgorithms {
public static void main(String[] args) {
RandomGeneratorFactory.all()
.sorted(Comparator.comparing(RandomGeneratorFactory::name))
.forEach(f -> System.out.printf("%-24s state bits=%-5d splittable=%-5b jumpable=%-5b%s%n",
f.name(), f.stateBits(), f.isSplittable(), f.isJumpable(), f.isDeprecated() ? " (deprecated)" : ""));
}
}
L128X1024MixRandom state bits=1152 splittable=true jumpable=false L128X128MixRandom state bits=256 splittable=true jumpable=false L128X256MixRandom state bits=384 splittable=true jumpable=false L32X64MixRandom state bits=96 splittable=true jumpable=false L64X1024MixRandom state bits=1088 splittable=true jumpable=false L64X128MixRandom state bits=192 splittable=true jumpable=false L64X128StarStarRandom state bits=192 splittable=true jumpable=false L64X256MixRandom state bits=320 splittable=true jumpable=false Random state bits=48 splittable=false jumpable=false SecureRandom state bits=2147483647 splittable=false jumpable=false SplittableRandom state bits=64 splittable=true jumpable=false Xoroshiro128PlusPlus state bits=128 splittable=false jumpable=true Xoshiro256PlusPlus state bits=256 splittable=false jumpable=true
RandomGenerator.of(name), or with RandomGeneratorFactory.of(name).create(seed) when you need a reproducible sequence - for simulations, tests and games which must replay exactly -
Reproducible sequences from a seed
//SeededDemo.java
import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;
public class SeededDemo {
public static void main(String[] args) {
RandomGeneratorFactory<RandomGenerator> factory = RandomGeneratorFactory.of("L64X128MixRandom");
RandomGenerator first = factory.create(2021);
RandomGenerator second = factory.create(2021);
System.out.println("Run 1: " + first.ints(5, 0, 100).boxed().toList());
System.out.println("Run 2: " + second.ints(5, 0, 100).boxed().toList()); // same seed, same numbers
RandomGenerator xoshiro = RandomGenerator.of("Xoshiro256PlusPlus"); // unseeded: different every run
System.out.println("Xoshiro gaussian in a sensible range: " + (Math.abs(xoshiro.nextGaussian()) < 10));
}
}
Run 1: [61, 48, 22, 24, 14] Run 2: [61, 48, 22, 24, 14] Xoshiro gaussian in a sensible range: true
- Splittable generators (
RandomGenerator.SplittableGenerator, such as the LXM algorithms) cansplit()off a new generator which is statistically independent - ideal for fork/join tasks and parallel streams, where each sub-task splits its own. - Jumpable generators (such as
Xoshiro256PlusPlus) canjump()far ahead in their sequence, so a set of generators can be created which are guaranteed not to overlap.
Splitting a generator for parallel tasks
//SplitDemo.java
import java.util.random.RandomGenerator.SplittableGenerator;
import java.util.random.RandomGeneratorFactory;
public class SplitDemo {
public static void main(String[] args) {
SplittableGenerator root = (SplittableGenerator) RandomGeneratorFactory.of("L64X128MixRandom").create(7);
// One independent generator per parallel task, all derived from one seed
double estimate = root.splits(8)
.parallel()
.mapToDouble(rng -> {
int inside = 0, samples = 250_000;
for (int i = 0; i < samples; i++) {
double x = rng.nextDouble(), y = rng.nextDouble();
if (x * x + y * y <= 1) inside++;
}
return 4.0 * inside / samples;
})
.average().orElse(0);
System.out.printf("Monte Carlo estimate of pi from 8 parallel generators: %.2f%n", estimate);
}
}
Monte Carlo estimate of pi from 8 parallel generators: 3.14
root.splits(8) returns a stream of 8 generators split from the root. Each parallel task gets its own, so there is no contention, the sequences are independent, and the whole result is reproducible from the single seed 7.
Which generator should you use?
- Security (passwords, tokens, keys): always
SecureRandom. None of the other generators are cryptographically secure. - General purpose:
RandomGenerator.getDefault(), orThreadLocalRandom.current()in multi-threaded code. - Reproducible simulations: a named LXM algorithm created with a seed, and split for parallel work.
- Existing code using
Randomkeeps working unchanged; change its declarations toRandomGeneratorto make the algorithm easy to swap later.
RandomGenerator interface, and add the LXM and Xoshiro families of algorithms. We wrote code against the interface, listed the algorithms and their properties with RandomGeneratorFactory, created reproducible sequences from a seed, and split one generator into independent generators for a parallel Monte Carlo calculation. Lastly, we went through which generator to use. Next, read about the Java 17 API additionsClick to read tutorial: Java 17 API Additions: HexFormat, InstantSource and More.Continue Reading Java 12 to 17 Tutorials
Overview of New Features in Java 17Click to read tutorial: Overview of New Features in Java 17 Java 17 Sealed Classes Tutorial with ExamplesClick to read tutorial: Java 17 Sealed Classes Tutorial with Examples Java 17 API Additions: HexFormat, InstantSource and MoreClick to read tutorial: Java 17 API Additions: HexFormat, InstantSource and More Overview of New Features in Java 12Click to read tutorial: Overview of New Features in Java 12 Overview of New Features in Java 13Click to read tutorial: Overview of New Features in Java 13 Overview of New Features in Java 14Click to read tutorial: Overview of New Features in Java 14 Overview of New Features in Java 15Click to read tutorial: Overview of New Features in Java 15 Overview of New Features in Java 16Click to read tutorial: Overview of New Features in Java 16 Java 12 API Additions: String.indent, teeing, mismatchClick to read tutorial: Java 12 API Additions: String.indent, teeing, mismatch
Overview of New Features in Java 17Click to read tutorial: Overview of New Features in Java 17 Java 17 Sealed Classes Tutorial with ExamplesClick to read tutorial: Java 17 Sealed Classes Tutorial with Examples Java 17 API Additions: HexFormat, InstantSource and MoreClick to read tutorial: Java 17 API Additions: HexFormat, InstantSource and More Overview of New Features in Java 12Click to read tutorial: Overview of New Features in Java 12 Overview of New Features in Java 13Click to read tutorial: Overview of New Features in Java 13 Overview of New Features in Java 14Click to read tutorial: Overview of New Features in Java 14 Overview of New Features in Java 15Click to read tutorial: Overview of New Features in Java 15 Overview of New Features in Java 16Click to read tutorial: Overview of New Features in Java 16 Java 12 API Additions: String.indent, teeing, mismatchClick to read tutorial: Java 12 API Additions: String.indent, teeing, mismatch