1. 概述
本教程将学习Java中生成随机数的不同方法。
2. 使用原生 Java 实现
The Java API provides us with several ways to achieve our purpose. Let’s see some of them.
2.1. java.lang.Math
Math.random 返回一个 0.0(含) 到 1.0(不含)范围内的随机数。下面返回 [min, max)区间的随机数:
int randomWithMathRandom = (int) ((Math.random() * (max - min)) + min);
2.2. java.util.Random
Before Java 1.7, the most popular way of generating random numbers was using nextInt. There were two ways of using this method, with and without parameters. The no-parameter invocation returns any of the int values with approximately equal probability. So, it’s very likely that we’ll get negative numbers:
Random random = new Random();
int randomWithNextInt = random.nextInt();
If we use the netxInt invocation with the bound parameter, we’ll get numbers within a range:
int randomWintNextIntWithinARange = random.nextInt(max - min) + min;
This will give us a number between 0 (inclusive) and parameter (exclusive). So, the bound parameter must be greater than 0. Otherwise, we’ll get a java.lang.IllegalArgumentException.
Java 8 introduced the new ints methods that return a java.util.stream.IntStream. Let’s see how to use them.
The ints method without parameters returns an unlimited stream of int values:
IntStream unlimitedIntStream = random.ints();
We can also pass in a single parameter to limit the stream size:
IntStream limitedIntStream = random.ints(streamSize);
And, of course, we can set the maximum and minimum for the generated range:
IntStream limitedIntStreamWithinARange = random.ints(streamSize, min, max);
2.3. java.util.concurrent.ThreadLocalRandom
Java 1.7 release brought us a new and more efficient way of generating random numbers via the ThreadLocalRandom class. This one has three important differences from the Random class:
- We don’t need to explicitly initiate a new instance of ThreadLocalRandom. This helps us to avoid mistakes of creating lots of useless instances and wasting garbage collector time
- We can’t set the seed for ThreadLocalRandom, which can lead to a real problem. If we need to set the seed, then we should avoid this way of generating random numbers
- Random class doesn’t perform well in multi-threaded environments
Now, let’s see how it works:
int randomWithThreadLocalRandomInARange = ThreadLocalRandom.current().nextInt(min, max);
With Java 8 or above, we have new possibilities. Firstly, we have two variations for the nextInt method:
int randomWithThreadLocalRandom = ThreadLocalRandom.current().nextInt();
int randomWithThreadLocalRandomFromZero = ThreadLocalRandom.current().nextInt(max);
Secondly, and more importantly, we can use the ints method:
IntStream streamWithThreadLocalRandom = ThreadLocalRandom.current().ints();
2.4. java.util.SplittableRandom
Java 8 has also brought us a really fast generator — the SplittableRandom class.
As we can see in the JavaDoc, this is a generator for use in parallel computations. It’s important to know that the instances are not thread-safe. So, we have to take care when using this class.
We have available the nextInt and ints methods. With nextInt we can set directly the top and bottom range using the two parameters invocation:
SplittableRandom splittableRandom = new SplittableRandom();
int randomWithSplittableRandom = splittableRandom.nextInt(min, max);
This way of using checks that the max parameter is bigger than min. Otherwise, we’ll get an IllegalArgumentException. However, it doesn’t check if we work with positive or negative numbers. So, any of the parameters can be negative. Also, we have available one- and zero-parameter invocations. Those work in the same way as we have described before.
We have available the ints methods, too. This means that we can easily get a stream of int values. To clarify, we can choose to have a limited or unlimited stream. For a limited stream, we can set the top and bottom for the number generation range:
IntStream limitedIntStreamWithinARangeWithSplittableRandom = splittableRandom.ints(streamSize, min, max);
2.5. java.security.SecureRandom
If we have security-sensitive applications, we should consider using SecureRandom. This is a cryptographically strong generator. Default-constructed instances don’t use cryptographically random seeds. So, we should either:
- Set the seed — consequently, the seed will be unpredictable
- Set the java.util.secureRandomSeed system property to true
This class inherits from java.util.Random. So, we have available all the methods we saw above. For example, if we need to get any of the int values, then we’ll call nextInt without parameters:
SecureRandom secureRandom = new SecureRandom();
int randomWithSecureRandom = secureRandom.nextInt();
On the other hand, if we need to set the range, we can call it with the bound parameter:
int randomWithSecureRandomWithinARange = secureRandom.nextInt(max - min) + min;
We must remember that this way of using it throws IllegalArgumentException if the parameter is not bigger than zero.
3. Using Third-Party APIs
As we have seen, Java provides us with a lot of classes and methods for generating random numbers. However, there are also third-party APIs for this purpose.
We’re going to take a look at some of them.
3.1. org.apache.commons.math3.random.RandomDataGenerator
There are a lot of generators in the commons mathematics library from the Apache Commons project. The easiest, and probably the most useful, is the RandomDataGenerator. It uses the Well19937c algorithm for the random generation. However, we can provide our algorithm implementation.
Let’s see how to use it. Firstly, we have to add dependency:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-math3</artifactId>
<version>3.6.1</version>
</dependency>
The latest version of commons-math3 can be found on Maven Central.
Then we can start working with it:
RandomDataGenerator randomDataGenerator = new RandomDataGenerator();
int randomWithRandomDataGenerator = randomDataGenerator.nextInt(min, max);
3.2. it.unimi.dsi.util.XoRoShiRo128PlusRandom
Certainly, this is one of the fastest random number generator implementations. It has been developed at the Information Sciences Department of the Milan University.
The library is also available at Maven Central repositories. So, let’s add the dependency:
<dependency>
<groupId>it.unimi.dsi</groupId>
<artifactId>dsiutils</artifactId>
<version>2.6.0</version>
</dependency>
This generator inherits from java.util.Random. However, if we take a look at the JavaDoc, we realize that there’s only one way of using it — through the nextInt method. Above all, this method is only available with the zero- and one-parameter invocations. Any of the other invocations will directly use the java.util.Random methods.
For example, if we want to get a random number within a range, we would write:
XoRoShiRo128PlusRandom xoroRandom = new XoRoShiRo128PlusRandom();
int randomWithXoRoShiRo128PlusRandom = xoroRandom.nextInt(max - min) + min;
4. 总结
There are several ways to implement random number generation. However, there is no best way. Consequently, we should choose the one that best suits our needs.
The full example can be found over on GitHub.