Random Numbers

Su Suriya Ravichandran Updated 14 Nov 2026
6 min read ·Lesson 111 of 133

Programs use random numbers to shuffle quiz questions, simulate dice, pick sample data, add jitter to retry delays, generate test data, and create one-time passwords and tokens. Java offers several generators, and the key decision is not how to get a random number but which kind: a fast, repeatable pseudo-random sequence for simulations and games, or a cryptographically secure source for anything an attacker might try to guess.

Pseudo-Random Numbers

Most generators are pseudo-random: they compute a sequence of numbers from a starting value called the seed, using a deterministic formula. The output passes statistical tests for randomness, but the same seed always produces the same sequence:

Random r1 = new Random(42);
Random r2 = new Random(42);

System.out.println(r1.nextInt(100) + " " + r1.nextInt(100));   // 30 63
System.out.println(r2.nextInt(100) + " " + r2.nextInt(100));   // 30 63: identical

A fixed seed is valuable for tests, reproducible simulations, and debugging. Without a seed (new Random()), the generator seeds itself from changing system values, so each run differs.

Math.random()

double x = Math.random();                    // 0.0 ≤ x < 1.0
int dice = (int) (Math.random() * 6) + 1;    // 1 to 6

Math.random() is convenient for a quick experiment, but it only produces doubles, cannot be seeded, and requires manual scaling. Prefer a generator object.

java.util.Random

import java.util.Random;

Random rnd = new Random();

rnd.nextInt();            // any int, including negatives
rnd.nextInt(100);         // 0 to 99
rnd.nextInt(1, 7);        // 1 to 6: origin inclusive, bound exclusive (Java 17+)
rnd.nextLong();
rnd.nextDouble();         // 0.0 ≤ x < 1.0
rnd.nextDouble(5.0, 10.0);   // 5.0 ≤ x < 10.0 (Java 17+)
rnd.nextBoolean();
rnd.nextGaussian();       // normal distribution: mean 0, standard deviation 1

The upper bound is always exclusive. nextInt(1, 7) simulates a six-sided die; nextInt(1, 6) would never roll a six.

On older Java versions without the two-argument form, compute the range manually: min + rnd.nextInt(max - min + 1).

Streams of random numbers

int[] scores = new Random(1).ints(5, 0, 101).toArray();      // five scores from 0 to 100
List<Double> ds = new Random(1).doubles(3).boxed().toList();

ints, longs, and doubles return streams, useful for generating test data.

Shuffling and picking

List<String> questions = new ArrayList<>(List.of("Q1", "Q2", "Q3", "Q4", "Q5", "Q6"));
Collections.shuffle(questions, new Random(2026));      // repeatable shuffle
String pick = questions.get(rnd.nextInt(questions.size()));   // a random element

Passing a seeded Random to shuffle makes the order reproducible.

ThreadLocalRandom

Random is thread-safe, but threads sharing one instance compete for it, which slows highly concurrent code. ThreadLocalRandom gives each thread its own generator:

import java.util.concurrent.ThreadLocalRandom;

int delayMs = ThreadLocalRandom.current().nextInt(100, 500);

Always call ThreadLocalRandom.current() at the point of use; do not store the result in a field shared between threads. It cannot be seeded, so it is for production randomness rather than reproducible tests.

SecureRandom: For Security

Random, ThreadLocalRandom, and Math.random() are predictable. java.util.Random uses a 48-bit linear formula, and an attacker who observes a few outputs can calculate all future ones. They must never be used for:

  • passwords, password-reset tokens, and one-time passwords (OTPs),
  • session IDs and API keys,
  • encryption keys, salts, and nonces,
  • coupon codes, invitation links, or anything else that grants access or value when guessed.

java.security.SecureRandom draws on unpredictable entropy from the operating system and is designed so that its outputs cannot be predicted:

import java.security.SecureRandom;

SecureRandom secure = new SecureRandom();
int digit = secure.nextInt(10);
byte[] token = new byte[32];
secure.nextBytes(token);                    // 256 random bits
String encoded = Base64.getUrlEncoder().withoutPadding().encodeToString(token);

SecureRandom is a subclass of Random, so it has the same methods. Create one instance and reuse it (it is thread-safe); creating a new one for every value is unnecessary. It is somewhat slower than Random, which is irrelevant at the rate tokens are created.

For unique identifiers that need not be secret, UUID.randomUUID() produces a 36-character random UUID, generated internally with SecureRandom.

RandomGenerator (Java 17+)

Java 17 introduced the java.util.random.RandomGenerator interface, implemented by Random, SecureRandom, ThreadLocalRandom, and a family of newer, higher-quality algorithms:

import java.util.random.RandomGenerator;

RandomGenerator g = RandomGenerator.of("L64X128MixRandom");
int n = g.nextInt(1, 101);

RandomGenerator dflt = RandomGenerator.getDefault();

Writing methods against RandomGenerator lets callers supply whichever generator suits them: a seeded one in tests, a fast one in simulations, a secure one where needed.

Choosing a Generator

NeedUse
Reproducible sequence for tests, simulations, or demosnew Random(seed)
General-purpose randomness in single-threaded codenew Random() or RandomGenerator.getDefault()
Randomness in highly concurrent codeThreadLocalRandom.current()
Tokens, OTPs, passwords, keys, coupon codesSecureRandom
Unique IDsUUID.randomUUID()
High-quality statistical generatorsRandomGenerator.of("L64X128MixRandom") and related algorithms

Practical Example: Quiz Shuffling, Dice, and Secure Codes

import java.security.SecureRandom;
import java.util.*;

public class Rnd {
    private static final SecureRandom SECURE = new SecureRandom();
    private static final String ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";   // no 0/O, 1/I

    static String otp(int digits) {
        StringBuilder sb = new StringBuilder(digits);
        for (int i = 0; i < digits; i++) sb.append(SECURE.nextInt(10));
        return sb.toString();
    }

    static String couponCode(int length) {
        StringBuilder sb = new StringBuilder(length);
        for (int i = 0; i < length; i++) sb.append(ALPHABET.charAt(SECURE.nextInt(ALPHABET.length())));
        return sb.toString();
    }

    public static void main(String[] args) {
        Random seeded = new Random(42);
        System.out.println(seeded.nextInt(100) + " " + seeded.nextInt(100) + " " + seeded.nextInt(1, 7));
        Random again = new Random(42);
        System.out.println(again.nextInt(100) + " " + again.nextInt(100) + " " + again.nextInt(1, 7));

        List<String> questions = new ArrayList<>(List.of("Q1", "Q2", "Q3", "Q4", "Q5", "Q6"));
        Collections.shuffle(questions, new Random(2026));
        System.out.println("Quiz order: " + questions);

        int[] counts = new int[7];
        Random dice = new Random(7);
        for (int i = 0; i < 60_000; i++) counts[dice.nextInt(1, 7)]++;
        System.out.println("Dice: " + Arrays.toString(Arrays.copyOfRange(counts, 1, 7)));

        String code = otp(6);
        String coupon = couponCode(8);
        System.out.println("OTP length " + code.length() + ", digits only: " + code.matches("\\d{6}"));
        System.out.println("Coupon length " + coupon.length() + ", valid: " + coupon.matches("[A-HJ-NP-Z2-9]{8}"));
    }
}

Output (Java 17 or later):

30 63 1
30 63 1
Quiz order: [Q3, Q2, Q5, Q4, Q1, Q6]
Dice: [10018, 9900, 9996, 9938, 10109, 10039]
OTP length 6, digits only: true
Coupon length 8, valid: true

The first two lines prove that two Random objects with the same seed produce the same sequence. That repeatability is also what makes the quiz shuffle reproducible: every run with seed 2026 gives the same question order, so a teacher could regenerate an exam paper exactly. Remove the seed to give each student a different order.

The dice simulation rolls 60,000 times. Each face appears close to the expected 10,000 times, showing that nextInt(1, 7) covers all six faces evenly; index 0 of counts is unused, which is why the program prints only indexes 1 to 6.

The OTP and coupon code use SecureRandom, because they protect accounts and money. The coupon alphabet leaves out the easily confused characters 0, O, 1, and I, which matters when people type codes by hand. The program prints only whether the generated values have the right form, because their actual contents change on every run and are unpredictable, exactly as they should be.

0 Comments

Reviewed before they appear

No comments yet.

Java
Java Introduction Java Features Java Syntax Java Comments Java Statements Java Output Java Variables Java Data Types Java Type Casting Java Operators Java Strings Java Numbers Java Math Java Booleans Java User Input Java Conditions Java If Else Java Switch Java While Loop Java For Loop Java Break Java Continue Java Arrays Java Methods Java Parameters Java Return Values Java Scope Java Recursion Install Java Install JDK Java IDEs Java JDK Java JRE Java JVM Java Compiler Java File Compile Java Run Java JShell Basics Java Packages Java OOP Java Classes Java Objects Class Methods Constructors Constructor Overload this Keyword static Keyword final Keyword Encapsulation Inheritance Polymorphism Abstraction Access Modifiers super Keyword Method Overload Method Override Instanceof Operator Nested Classes Inner Classes Anonymous Classes Enums Records Array Basics Array Length Loop Arrays Multidimensional Arrays Array Copy Array Sorting Array Search ArrayList LinkedList HashSet TreeSet HashMap TreeMap Queue Interface Deque Interface Stack Class Iterator List Interface Set Interface Map Interface Collections Class Comparable Comparator Generics Basics Generic Classes Generic Methods Wildcards String Basics String Length String Methods String Concatenation String Compare String Search String Replace String Split String Format StringBuilder StringBuffer Text Blocks Regular Expressions Number Types Integer Class Float Class Double Class BigInteger BigDecimal Math Class Random Numbers Number Format Date Basics LocalDate LocalTime LocalDateTime ZonedDateTime Date Formatting Date Parsing Period Class Duration Class Java Instant Class Java Exception Basics Java try...catch Java Multiple Catch Blocks Java finally Block Java throw Keyword Java throws Keyword Java Checked Exceptions Java Unchecked Exceptions Java Custom Exceptions Java try-with-resources Java Exception Chaining
Ask about this post
AI Ask about this post

Ask questions about Random Numbers and get answers drawn from it.

Signed-in readers only.