Generate a Random Number in a Range in Java (Min to Max)

Generate a random number in a range in Java with nextInt(min, max + 1), legacy formulas, doubles, unique values, exclusions and secure codes.

Number line from 1 to 7 where nextInt(1, 7) can return 1 to 6 and never the bound 7

To generate a random number in a range in Java, we call nextInt(origin, bound) on a random number generator, where the origin (the minimum) is included and the bound is excluded, so a range from min to max with both ends included needs nextInt(min, max + 1). The same pattern works for long, float and double values with nextLong(), nextFloat() and nextDouble().

We need a random number in a range for dice and game logic, test data such as ages or prices, retry delays with jitter, sampling, and one-time codes with a fixed number of digits.

The following example picks numbers from several ranges.

int dice = ThreadLocalRandom.current().nextInt(1, 7);                 // 1 to 6
int percent = RandomGenerator.getDefault().nextInt(0, 101);           // 0 to 100
double price = ThreadLocalRandom.current().nextDouble(5.0, 10.0);     // 5.0 to just under 10.0
long orderNo = RandomGenerator.getDefault().nextLong(1_000L, 10_000L);  // 1000 to 9999
int java8Dice = 1 + new Random().nextInt(6);                           // 1 to 6, works on Java 8

Notice that each integer bound is one more than the largest value we want, such as 7 for a dice. We explain the bounds first, list which Java version added which method, show the formulas for older code and their traps, and cover decimals, unique values, excluded values and a one-time code.

1. Origin, Bound and the Missing Upper Value

The range methods of RandomGenerator take an origin and a bound. The origin is the smallest possible result and the bound is the first value that is never returned, so nextInt(1, 7) picks one of the six values 1, 2, 3, 4, 5 and 6, each with the same probability.

Number line from 1 to 7 where nextInt(1, 7) can return 1 to 6 and never the bound 7
The origin is included and the bound is excluded, so a dice roll needs nextInt(1, 7)

A method with an inclusive maximum reads better at the call site and keeps the +1 in one place. Adding 1 to Integer.MAX_VALUE overflows to a negative number, so the helper does the arithmetic in long.

static int randomBetween(RandomGenerator generator, int min, int max) {
    if (min > max) {
        throw new IllegalArgumentException("min " + min + " is greater than max " + max);
    }
    return (int) generator.nextLong(min, (long) max + 1);
}
int roll = randomBetween(ThreadLocalRandom.current(), 1, 6);                 // 1 to 6
int anyPositive = randomBetween(ThreadLocalRandom.current(), 0, Integer.MAX_VALUE);  // 0 to 2147483647
int overflow = ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE + 1); // IllegalArgumentException: bound must be greater than origin
int reversed = ThreadLocalRandom.current().nextInt(10, 5);                    // IllegalArgumentException: bound must be greater than origin

Every range method throws an IllegalArgumentException when the bound is not greater than the origin. A range with swapped arguments or an overflowed bound fails on the first call instead of returning wrong values, so we see the bug early.

2. Which Java Version Added Each Range Method

Range methods appeared in several steps, which is why older tutorials show different code. The two-argument nextInt(origin, bound) on Random and SecureRandom came with the RandomGenerator interface of JEP 356 in Java 17, not in Java 8.

MethodAvailable onSince
nextInt(origin, bound), nextLong(), nextDouble()ThreadLocalRandomJava 7
nextInt(origin, bound) and the other range methodsSplittableRandomJava 8
ints(count, origin, bound), longs(), doubles()Random, SecureRandom, ThreadLocalRandomJava 8
nextInt(origin, bound), nextLong(), nextDouble(), nextFloat()Every RandomGenerator, including Random and SecureRandomJava 17
nextInt(bound) (origin 0)RandomJava 1.2

On Java 17 or later, we use the range methods on any generator. On Java 8 to 16, we use ThreadLocalRandom or SplittableRandom for ranges, or the formula in the next section with Random. The differences between the generator classes are covered in generating random numbers in Java.

3. Range Formulas for Older Code

Before Java 17, a range with Random meant shifting a zero-based value. The call nextInt(n) returns 0 to n – 1, so we pass the size of the range, max – min + 1, and add min. The Math.random() formula does the same with a double from 0.0 up to, but excluding, 1.0.

Random random = new Random();
int fromRandom = random.nextInt(180 - 60 + 1) + 60;          // 60 to 180
int fromMath = 60 + (int) (Math.random() * (180 - 60 + 1));  // 60 to 180
int wrongFormula = 60 + (int) (Math.random() * 180);         // 60 to 239, a common bug

The last line multiplies by max instead of by the size of the range, which shifts the upper end to 239. Another common bug is Math.abs(random.nextInt()) % 6, which returns a negative value when nextInt() returns Integer.MIN_VALUE, and favors small results slightly because 231 is not a multiple of 6. The methods nextInt(bound) and nextInt(origin, bound) avoid both problems.

4. Random double, float and long Values in a Range

For decimal values, nextDouble(origin, bound) and nextFloat(origin, bound) return any value from the origin up to, but excluding, the bound. Hitting a single exact double value is so unlikely that the excluded bound does not matter in practice, so we pass the upper end as it is.

RandomGenerator generator = RandomGenerator.getDefault();
double temperature = generator.nextDouble(-5.0, 35.0);       // -5.0 to just under 35.0
float discount = generator.nextFloat(0.05f, 0.30f);          // 0.05 to just under 0.30
long fileSize = generator.nextLong(1L, 10_000_000_000L);     // 1 to 9999999999
int offset = generator.nextInt(-10, 11);                     // -10 to 10

A test data builder for an online shop needs prices from 1.00 to 99.99 with two decimal places. Instead of rounding a random double, we pick a whole number of cents and convert it to BigDecimal, so every price is exact and both ends are reachable.

long cents = ThreadLocalRandom.current().nextLong(100, 10_000);    // 100 to 9999
BigDecimal price = BigDecimal.valueOf(cents, 2);                    // 1.00 to 99.99

Rounding a random double also works for display, as described in rounding to 2 decimal places, but the rounded ends get only half the probability of the other values.

5. Many Random Numbers in a Range at Once

The stream methods ints(), longs() and doubles() take a count, an origin and a bound, and they return a stream of random values in that range. We collect them into an array or a list, or process them further.

int[] rolls = new Random(42).ints(5, 1, 7).toArray();              // [3, 4, 1, 3, 1]
double[] weights = new Random(42).doubles(3, 0.5, 2.0).toArray();    // 3 values from 0.5 to just under 2.0

A quiz app shows 5 questions out of a pool of 40, and no question may appear twice. Random numbers in a range can repeat, so we shuffle all 40 IDs and take the first 5, which picks each combination with the same probability. The seed 3 only makes the result repeatable in a test.

List<Integer> questionIds = IntStream.rangeClosed(1, 40)
        .boxed()
        .collect(Collectors.toCollection(ArrayList::new));
Collections.shuffle(questionIds, new Random(3));
List<Integer> quiz = List.copyOf(questionIds.subList(0, 5));         // [4, 36, 22, 18, 6]

For a few values from a huge range, such as 5 numbers from 1 to 1,000,000, shuffling a million IDs wastes memory. There, generator.ints(1, 1_000_001).distinct().limit(5) draws until it has 5 different values.

6. Excluding Values From a Range

Some features need a random value that differs from the last one, such as a tip of the day that must not repeat yesterday’s tip. Drawing again until the value differs works, but it has no upper limit on the number of draws. A cleaner way draws from a range that is one value smaller and shifts every result at or above the excluded value up by one.

static int randomExcluding(RandomGenerator generator, int min, int max, int excluded) {
    int value = generator.nextInt(min, max);
    return value >= excluded ? value + 1 : value;
}
int nextTip = randomExcluding(ThreadLocalRandom.current(), 1, 6, 3);     // 1 to 6, never 3
boolean neverThree = IntStream.range(0, 10_000)
        .map(i -> randomExcluding(ThreadLocalRandom.current(), 1, 6, 3))
        .noneMatch(v -> v == 3);
boolean checked = neverThree;                                             // true

The method works for an inclusive range from min to max and one excluded value inside it, and each of the remaining values has the same probability. For several excluded values, we put the allowed values in a list and pick a random index with nextInt(list.size()).

7. Real-World Example With a One-Time Code

A login page sends a 6-digit code by SMS. The code must be unpredictable, so we use SecureRandom and not Random, whose next values can be computed from a few earlier ones.

SecureRandom secure = new SecureRandom();
int code = secure.nextInt(100_000, 1_000_000);                // 100000 to 999999
String padded = String.format("%06d", secure.nextInt(1_000_000));  // 000000 to 999999

The first line never starts with a zero, so it uses only 900,000 of the possible codes. The second line pads with zeros and uses all 1,000,000, which is the usual choice for codes that people type. The setup of SecureRandom itself is covered in secure random number generation.

8. Random Number in a Range FAQs

An off-by-one maximum, Java 8 syntax and negative ranges each change how we call the range methods.

8.1. How do we generate a random number between 1 and 10?

We call ThreadLocalRandom.current().nextInt(1, 11). The bound 11 is excluded, so the result is 1 to 10. On Java 8 with Random, the same range is 1 + random.nextInt(10).

8.2. Is the upper bound of nextInt() inclusive?

No. Every nextInt(), nextLong(), nextFloat() and nextDouble() range method excludes the bound and includes the origin. The stream methods ints(), longs() and doubles() follow the same rule.

8.3. Should we use Random or ThreadLocalRandom for a range?

We use ThreadLocalRandom in application code, because it needs no shared instance and has range methods since Java 7. We use Random with a seed when a test needs the same values on every run.

8.4. How do we get a random number between two negative numbers?

The range methods accept negative values, so nextInt(-20, -9) returns -20 to -10. The origin must be the smaller number, otherwise the call throws an IllegalArgumentException.

9. Conclusion

A random number in a range comes from nextInt(origin, bound) and its long, float and double versions, where the origin is included and the bound is excluded. For an inclusive maximum, we add 1 in a helper that uses long arithmetic, so Integer.MAX_VALUE works too.

The methods exist on every generator since Java 17 and on ThreadLocalRandom since Java 7, while older Random code uses nextInt(max – min + 1) + min. For values without repeats we shuffle, for excluded values we shift, and for codes and tokens we use SecureRandom. More beginner tasks are collected in the Java tutorial.

10. References

Happy Learning !!

Source Code on Github

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.