Générer des nombres aléatoires en Java

1. Vue d'ensemble

Dans ce didacticiel, nous explorerons différentes manières de générer des nombres aléatoires en Java.

2. Utilisation de l'API Java

L'API Java nous offre plusieurs moyens d'atteindre notre objectif. Voyons certains d'entre eux.

2.1. java.lang.Math

La méthode aléatoire de la classe Math renverra une valeur double dans une plage de 0,0 (inclus) à 1,0 (exclusif). Voyons comment nous l'utiliser pour obtenir un nombre aléatoire dans une plage donnée définie par min et max :

int randomWithMathRandom = (int) ((Math.random() * (max - min)) + min);

2.2. java.util.Random

Avant Java 1.7, le moyen le plus populaire de générer des nombres aléatoires était d'utiliser nextInt . Il y avait deux manières d'utiliser cette méthode, avec et sans paramètres. L'appel sans paramètre renvoie l'une des valeurs int avec une probabilité approximativement égale. Donc, il est très probable que nous obtenions des nombres négatifs:

Random random = new Random(); int randomWithNextInt = random.nextInt();

Si nous utilisons l' invocation netxInt avec le paramètre lié , nous obtiendrons des nombres dans une plage:

int randomWintNextIntWithinARange = random.nextInt(max - min) + min;

Cela nous donnera un nombre entre 0 (inclus) et paramètre (exclusif). Ainsi, le paramètre lié doit être supérieur à 0. Sinon, nous obtiendrons une exception java.lang.IllegalArgumentException .

Java 8 a introduit les nouvelles méthodes ints qui renvoient un java.util.stream.IntStream. Voyons comment les utiliser.

La méthode ints sans paramètres renvoie un flux illimité de valeurs int :

IntStream unlimitedIntStream = random.ints();

On peut également passer un seul paramètre pour limiter la taille du flux:

IntStream limitedIntStream = random.ints(streamSize);

Et, bien sûr, nous pouvons définir le maximum et le minimum pour la plage générée:

IntStream limitedIntStreamWithinARange = random.ints(streamSize, min, max);

2.3. java.util.concurrent.ThreadLocalRandom

La version 1.7 de Java nous a apporté un nouveau moyen plus efficace de générer des nombres aléatoires via la classe ThreadLocalRandom . Celui-ci présente trois différences importantes par rapport à la classe Random :

  • Nous n'avons pas besoin de lancer explicitement une nouvelle instance de ThreadLocalRandom . Cela nous aide à éviter les erreurs de création de nombreuses instances inutiles et de perte de temps de ramasse-miettes
  • Nous ne pouvons pas définir la graine pour ThreadLocalRandom , ce qui peut conduire à un réel problème. Si nous devons définir la graine, nous devons éviter cette façon de générer des nombres aléatoires
  • La classe aléatoire ne fonctionne pas bien dans les environnements multi-threads

Maintenant, voyons comment cela fonctionne:

int randomWithThreadLocalRandomInARange = ThreadLocalRandom.current().nextInt(min, max);

Avec Java 8 ou supérieur, nous avons de nouvelles possibilités. Tout d'abord, nous avons deux variantes pour la méthode nextInt :

int randomWithThreadLocalRandom = ThreadLocalRandom.current().nextInt(); int randomWithThreadLocalRandomFromZero = ThreadLocalRandom.current().nextInt(max);

Deuxièmement, et plus important encore, nous pouvons utiliser la méthode ints :

IntStream streamWithThreadLocalRandom = ThreadLocalRandom.current().ints();

2.4. java.util.SplittableRandom

Java 8 nous a également apporté un générateur très rapide - la classe SplittableRandom .

Comme nous pouvons le voir dans le JavaDoc, il s'agit d'un générateur à utiliser dans les calculs parallèles. Il est important de savoir que les instances ne sont pas thread-safe. Donc, nous devons faire attention lors de l'utilisation de cette classe.

Nous disposons des méthodes nextInt et ints . Avec nextInt, nous pouvons définir directement la plage supérieure et inférieure en utilisant les deux paramètres d'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:

 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 

Ce générateur hérite de java.util.Random . Cependant, si nous jetons un coup d'œil à JavaDoc, nous nous rendons compte qu'il n'y a qu'une seule façon de l'utiliser - via la méthode nextInt . Surtout, cette méthode n'est disponible qu'avec les invocations à zéro et un paramètre. Tous les autres appels utiliseront directement les méthodes java.util.Random .

Par exemple, si nous voulons obtenir un nombre aléatoire dans une plage, nous écrirons:

XoRoShiRo128PlusRandom xoroRandom = new XoRoShiRo128PlusRandom(); int randomWithXoRoShiRo128PlusRandom = xoroRandom.nextInt(max - min) + min;

4. Conclusion

Il existe plusieurs façons d'implémenter la génération de nombres aléatoires. Cependant, il n'y a pas de meilleur moyen. Par conséquent, nous devons choisir celui qui correspond le mieux à nos besoins.

L'exemple complet peut être trouvé sur GitHub.