1. Información general
En este tutorial, exploraremos diferentes formas de generar números aleatorios en Java.
2. Usando la API de Java
La API de Java nos proporciona varias formas de lograr nuestro propósito. Veamos algunos de ellos.
2.1. java.lang.Math
El método aleatorio de la clase Math devolverá un valor doble en un rango de 0.0 (inclusive) a 1.0 (exclusivo). Veamos cómo lo usaríamos para obtener un número aleatorio en un rango dado definido por mínimo y máximo :
int randomWithMathRandom = (int) ((Math.random() * (max - min)) + min);
2.2. java.util.Random
Antes de Java 1.7, la forma más popular de generar números aleatorios era usar nextInt . Había dos formas de utilizar este método, con y sin parámetros. La invocación sin parámetro devuelve cualquiera de los valores int con aproximadamente la misma probabilidad. Entonces, es muy probable que obtengamos números negativos:
Random random = new Random(); int randomWithNextInt = random.nextInt();
Si usamos la invocación netxInt con el parámetro vinculado , obtendremos números dentro de un rango:
int randomWintNextIntWithinARange = random.nextInt(max - min) + min;
Esto nos dará un número entre 0 (inclusive) y parámetro (exclusivo). Entonces, el parámetro enlazado debe ser mayor que 0. De lo contrario, obtendremos una java.lang.IllegalArgumentException .
Java 8 introdujo los nuevos métodos ints que devuelven un java.util.stream.IntStream. Veamos cómo usarlos.
El método ints sin parámetros devuelve un flujo ilimitado de valores int :
IntStream unlimitedIntStream = random.ints();
También podemos pasar un solo parámetro para limitar el tamaño de la transmisión:
IntStream limitedIntStream = random.ints(streamSize);
Y, por supuesto, podemos establecer el máximo y el mínimo para el rango generado:
IntStream limitedIntStreamWithinARange = random.ints(streamSize, min, max);
2.3. java.util.concurrent.ThreadLocalRandom
La versión de Java 1.7 nos trajo una forma nueva y más eficiente de generar números aleatorios a través de la clase ThreadLocalRandom . Este tiene tres diferencias importantes con la clase Random :
- No necesitamos iniciar explícitamente una nueva instancia de ThreadLocalRandom . Esto nos ayuda a evitar errores al crear muchas instancias inútiles y perder el tiempo del recolector de basura.
- No podemos establecer la semilla para ThreadLocalRandom , lo que puede conducir a un problema real. Si necesitamos establecer la semilla, entonces debemos evitar esta forma de generar números aleatorios.
- La clase aleatoria no funciona bien en entornos de subprocesos múltiples
Ahora, veamos cómo funciona:
int randomWithThreadLocalRandomInARange = ThreadLocalRandom.current().nextInt(min, max);
Con Java 8 o superior, tenemos nuevas posibilidades. En primer lugar, tenemos dos variaciones para el método nextInt :
int randomWithThreadLocalRandom = ThreadLocalRandom.current().nextInt(); int randomWithThreadLocalRandomFromZero = ThreadLocalRandom.current().nextInt(max);
En segundo lugar, y más importante, podemos utilizar el método ints :
IntStream streamWithThreadLocalRandom = ThreadLocalRandom.current().ints();
2.4. java.util.SplittableRandom
Java 8 también nos ha traído un generador realmente rápido: la clase SplittableRandom .
Como podemos ver en el JavaDoc, este es un generador para usar en cálculos paralelos. Es importante saber que las instancias no son seguras para subprocesos. Entonces, debemos tener cuidado al usar esta clase.
Tenemos disponible el nextInt y Ints métodos. Con nextInt podemos establecer directamente el rango superior e inferior usando la invocación de dos parámetros:
SplittableRandom splittableRandom = new SplittableRandom(); int randomWithSplittableRandom = splittableRandom.nextInt(min, max);
Esta forma de uso comprueba que el parámetro max sea mayor que min . De lo contrario, obtendremos una IllegalArgumentException . Sin embargo, no comprueba si trabajamos con números positivos o negativos. Entonces, cualquiera de los parámetros puede ser negativo. Además, tenemos disponibles invocaciones de uno y cero parámetros. Esos funcionan de la misma manera que hemos descrito antes.
También tenemos disponibles los métodos ints . Esto significa que podemos obtener fácilmente un flujo de valores int . Para aclarar, podemos optar por tener una transmisión limitada o ilimitada. Para una transmisión limitada, podemos establecer la parte superior e inferior para el rango de generación de números:
IntStream limitedIntStreamWithinARangeWithSplittableRandom = splittableRandom.ints(streamSize, min, max);
2.5. java.security.SecureRandom
Si tenemos aplicaciones sensibles a la seguridad, deberíamos considerar el uso de SecureRandom . Este es un generador criptográficamente fuerte. Las instancias construidas por defecto no utilizan semillas criptográficamente aleatorias. Entonces, deberíamos:
- Establecer la semilla - en consecuencia, la semilla será impredecible
- Establezca la propiedad del sistema java.util.secureRandomSeed en true
Esta clase hereda de java.util.Random . Entonces, tenemos disponibles todos los métodos que vimos anteriormente. Por ejemplo, si necesitamos obtener alguno de los valores int , llamaremos a nextInt sin parámetros:
SecureRandom secureRandom = new SecureRandom(); int randomWithSecureRandom = secureRandom.nextInt();
Por otro lado, si necesitamos establecer el rango, podemos llamarlo con el parámetro vinculado :
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:
org.apache.commons commons-math3 3.6.1
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:
it.unimi.dsi dsiutils 2.6.0
Este generador hereda de java.util.Random . Sin embargo, si echamos un vistazo a JavaDoc, nos damos cuenta de que solo hay una forma de usarlo: a través del método nextInt . Sobre todo, este método solo está disponible con las invocaciones de un parámetro y de cero. Cualquiera de las otras invocaciones utilizará directamente los métodos java.util.Random .
Por ejemplo, si queremos obtener un número aleatorio dentro de un rango, escribiríamos:
XoRoShiRo128PlusRandom xoroRandom = new XoRoShiRo128PlusRandom(); int randomWithXoRoShiRo128PlusRandom = xoroRandom.nextInt(max - min) + min;
4. Conclusión
Hay varias formas de implementar la generación de números aleatorios. Sin embargo, no existe la mejor manera. En consecuencia, debemos elegir el que mejor se adapte a nuestras necesidades.
El ejemplo completo se puede encontrar en GitHub.