Java: generar enteros aleatorios en el rango

En este tutorial, repasaremos cómo generar enteros aleatorios en un rango específico en Java: valores únicos y múltiples, en una amplia variedad de formas, con ejemplos.

Introducción

En este tutorial, veremos cómo generar números enteros aleatorios en un rango específico en Java.

Echaremos un vistazo a varios enfoques, incluido el núcleo de Java y las soluciones de terceros:

Nota: Para cada enfoque, cubriremos cómo generar un número entero aleatorio así como cómo generar una secuencia de números enteros aleatorios.

Todos estos métodos son inclusivo de límite inferior y exclusivo de límite superior.

Aleatorio.ints() {#mentas aleatorias}

Comenzamos con Random.ints() que se agregó a la clase Random en Java 8, exactamente para este propósito. Java originalmente no tenía una solución completamente intuitiva para esta tarea, integrada.

El método ints() devuelve una secuencia de valores aleatorios, en forma de IntStream. Al ser una implementación Stream, no tiene límites:

1
2
Random random = new Random();
random.ints().forEach(System.out::println);

Esto resulta en:

1
2
3
4
5
6
-526320702
-744603161
474879020
1864311422
406782252
...

Este es un IntStream ilimitado, que generará cualquier valor desde Integer.MIN_VALUE hasta Integer.MAX_VALUE. Sin embargo, puedes especificar un rango, así como la cantidad de elementos que te gustaría generar.

Además, el primer argumento es la cantidad de elementos que le gustaría generar; de lo contrario, la secuencia generará una cantidad ilimitada de elementos, hasta que se agote el espacio de la memoria de montón:

1
2
3
4
5
List<Integer> intList = new Random().ints(5, 1, 11)
        .boxed()
        .collect(Collectors.toList());

System.out.println(intList);

Antes de recolectar el IntStream, tendremos que encasillarlo a través del método boxed(), que devuelve un flujo que consta de los elementos del IntStream, encuadrados en un Integer. Luego, ejecutamos collect() en el flujo devuelto, no en el original.

El método collect() de IntStream no devuelve una colección; ejecuta una [operación de reducción mutable](https://docs.oracle.com/javase/8/docs/api/java/ util/stream/package-summary.html#MutableReduction).

Ejecutar este código da como resultado:

1
[1, 9, 9, 6, 2]

Para generar un único entero aleatorio, simplemente puede modificar el primer argumento del método ints(), o usar los métodos findFirst() y getAsInt() para extraerlo de IntStream:

1
2
int randomInt = new Random().ints(1, 1, 11).findFirst().getAsInt();
System.out.println(randomInt);

Esto da como resultado un número entero aleatorio en el rango entre 1..10 (el segundo argumento es exclusivo):

1
5

Random.nextInt()

Un ejemplo más clásico que a menudo verás que la gente usa es simplemente utilizar el método Random.nextInt(). Acepta un parámetro bound, que establece el límite superior y establece el límite inferior en 0 de forma predeterminada.

Desafortunadamente, no le permite cambiar esto, por lo que se puede usar un “truco” rápido y simple para especificar los límites:

1
2
3
4
int min = 10;
int max = 100;

System.out.println(new Random().nextInt(max - min) + min);

Esto da como resultado un número entero aleatorio en el rango entre min y max:

1
53

Generar una secuencia de esto requeriría que llamemos al método varias veces:

1
2
3
4
5
6
7
8
public static List<Integer> intsInRange(int size, int lowerBound, int upperBound) {
    Random random = new Random();
    List<Integer> result = new ArrayList<>();
    for (int i = 0; i < size; i++) {
        result.add(random.nextInt(upperBound - lowerBound) + lowerBound);
    }
    return result;
}

Esta es una recreación limitada por Lista de la funcionalidad Random.ints(), que simplemente devuelve una lista de enteros generados aleatoriamente en un rango, con un tamaño dado:

1
2
List<Integer> integerList =  intsInRange(5, 0, 10);
System.out.println(integerList);

Ejecutar este código daría como resultado algo similar a:

1
[3, 8, 2, 2, 9]

SecureRandom.nextInt()

La clase SecureRandom es una alternativa a la clásica clase Random, pero proporciona un generador de números aleatorios criptográficamente fuerte. Random depende internamente del reloj del sistema para generar semillas numéricas, que no son verdaderamente aleatorias.

Por otro lado, SecureRandom toma muchos más datos aleatorios del entorno para generar una semilla mucho más aleatoria.

Si la seguridad criptográfica es algo que le preocupa, puede optar por usar SecureRandom en su lugar, que se comporta de la misma manera que Random desde el punto de vista del desarrollador:

1
2
3
4
5
int max = 100;
int min = 10;

int randomInt = new SecureRandom().nextInt(max - min) + min;
System.out.println(randomInt);

Lo que da como resultado un número entero aleatorio en el rango entre min y max:

1
95

Y si desea generar secuencias, se puede crear un método auxiliar:

1
2
3
4
5
6
7
8
public static List<Integer> intsInRange(int size, int lowerBound, int upperBound) {
    SecureRandom random = new SecureRandom();
    List<Integer> result = new ArrayList<>();
    for (int i = 0; i < size; i++) {
        result.add(random.nextInt(upperBound - lowerBound) + lowerBound);
    }
    return result;
}

Que puedes usar como:

1
2
List<Integer> integerList =  intsInRange3(5, 0, 10);
System.out.println(integerList);

Y que da como resultado:

1
[0, 9, 5, 6, 5]

Matemáticas.aleatorio() {#matemáticas al azar}

La clase Math nos proporciona excelentes métodos de ayuda relacionados con las matemáticas. Uno de ellos es el método Math.random(), que devuelve un valor aleatorio entre 0..1. Por lo general, se usa para generar valores percentiles aleatorios.

Sin embargo, de manera similar al truco Random.nextInt(), puede utilizar esta funcionalidad para generar cualquier número entero en un cierto rango:

1
2
3
4
5
int min = 10;
int max = 100;

int randomNumber = (int)(Math.random() * (max + 1 - min) + min);
System.out.println(randomNumber);

Sin embargo, este es incluso menos intuitivo que el enfoque anterior. Ejecutar este código da como resultado algo similar a:

1
43

Si desea trabajar con una secuencia, crearíamos un método auxiliar para agregar cada valor generado a una lista:

1
2
3
4
5
6
7
public static List<Integer> intsInRange(int size, int lowerBound, int upperBound) {
    List<Integer> result = new ArrayList<>();
    for (int i = 0; i < size; i++) {
        result.add((int)(Math.random() * (upperBound + 1 - lowerBound) + lowerBound));
    }
    return result;
}

Y entonces podemos llamarlo como:

1
2
List<Integer> integerList =  intsInRange(5, 0, 10);
System.out.println(integerList);

Que produce:

1
[9, 0, 3, 2, 0]

ThreadLocalRandom.nextInt()

Si está trabajando en un entorno de subprocesos múltiples, la clase ThreadLocalRandom está pensada para usarse como un equivalente seguro para subprocesos de Random. Afortunadamente, ofrece un método nextInt() con tanto un límite superior como uno inferior:

1
2
int randomInt = ThreadLocalRandom.current().nextInt(0, 10);
System.out.println(randomInt);

Como de costumbre, el límite inferior está incluido, mientras que el límite superior no lo está:

1
3

Del mismo modo, puede crear una función auxiliar para generar una secuencia de estos:

1
2
3
4
5
6
7
public static List<Integer> intsInRange(int size, int lowerBound, int upperBound) {
    List<Integer> result = new ArrayList<>();
    for (int i = 0; i < size; i++) {
        result.add(ThreadLocalRandom.current().nextInt(lowerBound, upperBound));
    }
    return result;
}

Que puedes usar como:

1
2
List<Integer> integerList = intsInRange4(5, 0, 10);
System.out.println(integerList);
1
[1, 9, 1, 9, 7]

SplittableRandom.ints()

Una clase menos conocida en la API de Java es la clase SplittableRandom, que se utiliza como generador de valores pseudoaleatorios. Como su nombre lo indica, se puede dividir y se ejecuta en paralelo, y en realidad solo se usa cuando tiene tareas que podrían dividirse nuevamente en subtareas más pequeñas.

Vale la pena señalar que esta clase también se basa en la generación de semillas no seguras: si está buscando una generación de semillas segura, use SecureRandom.

La clase ofrece un método ints(), que desde nuestra perspectiva, funciona de la misma manera que Random.ints():

1
2
3
4
5
List<Integer> intList = new SplittableRandom().ints(5, 1, 11)
        .boxed()
        .collect(Collectors.toList());

System.out.println(intList);

Lo que resulta en:

1
[3, 2, 8, 10, 3]

Y, si desea generar solo un número aleatorio, puede deshacerse del colector y usar findFirst() con getAsInt():

1
2
int randomInt = new SplittableRandom().ints(1, 1, 11).findFirst().getAsInt();
System.out.println(randomInt);

Lo que resulta en:

1
4

Conclusión

En este tutorial, hemos echado un vistazo exhaustivo a cómo generar números enteros aleatorios en rango en Java.

Hemos repasado el método más nuevo y útil, así como algunos otros métodos populares para terminar esta tarea. La mayoría de los enfoques se basan en las clases equivalentes Random o Random, utilizadas para contextos más específicos.

Licensed under CC BY-NC-SA 4.0