Générateur aléatoire en Java - comment choisir le bon ?

Denis Ribeiro .

14 octobre 2026

Schéma d'un générateur de nombres pseudo-aléatoires (GNPA) en Java, montrant la graine, les états internes et les fonctions de transformation pour produire des nombres aléatoires.

Un tirage au sort, un identifiant temporaire ou une simulation n’exigent pas le même type d’aléatoire en Java. Je présente ici les classes réellement utiles, les bonnes méthodes pour générer des nombres ou choisir des éléments, ainsi que les erreurs qui produisent des résultats biaisés ou fragiles.

Le bon générateur dépend surtout de l’usage prévu

  • Random convient aux exemples simples, jeux et simulations ordinaires.
  • ThreadLocalRandom est adapté aux applications concurrentes et aux serveurs.
  • SecureRandom doit être choisi pour les jetons, codes et données sensibles.
  • nextInt(bound) produit une valeur comprise entre zéro inclus et la limite exclue.
  • Une graine fixe rend les tests reproductibles, mais ne sécurise jamais un générateur.

Ce que signifie vraiment l’aléatoire en Java

Dans la plupart des programmes, Java ne produit pas un hasard absolu mais des suites pseudo-aléatoires. Un algorithme part d’un état initial, appelé graine, puis calcule des valeurs qui semblent imprévisibles dans un usage courant.

Cette distinction est importante. Pour mélanger des cartes virtuelles ou créer une variation dans une simulation, un générateur classique suffit. Pour créer un lien de réinitialisation de mot de passe, il faut une source conçue pour résister à la prédiction, même si elle est généralement plus coûteuse.

Je conseille donc de commencer par la question la plus simple : le résultat doit-il être imprévisible pour un attaquant ? Si la réponse est non, les outils de java.util sont généralement suffisants. Si la réponse est oui, il faut passer à SecureRandom.

Les principales solutions pour générer des valeurs

Java propose plusieurs options, chacune ayant un rôle précis. Le choix ne dépend pas seulement de la vitesse, mais aussi du nombre de threads, de la reproductibilité attendue et du niveau de sécurité.

Solution Usage recommandé Limite principale
Math.random() Petit calcul ou prototype rapide Peu flexible et peu lisible dès que le besoin grandit
Random Jeux, exercices, simulations simples Pas adapté à la sécurité
ThreadLocalRandom Code concurrent, serveurs, tâches parallèles Pas de garantie cryptographique
SplittableRandom Simulations et calculs parallèles reproductibles Ne doit pas servir à protéger un secret
SecureRandom Jetons, codes de validation, clés temporaires Plus lent et inutile pour un simple dé

La classe Random pour commencer

La classe Random reste pratique lorsqu’on veut comprendre le mécanisme sans ajouter de dépendance. Cet exemple tire un entier entre 1 et 6 inclus, comme un dé.

import java.util.Random;

Random random = new Random();
int resultat = random.nextInt(6) + 1;

La méthode nextInt(6) renvoie une valeur de 0 à 5. Le décalage de + 1 transforme donc l’intervalle en 1 à 6. C’est une petite règle, mais l’oubli de cette limite est l’une des erreurs que je vois le plus souvent chez les débutants.

Math.random pour un besoin minuscule

Math.random() renvoie un nombre décimal dans l’intervalle [0.0, 1.0[. On peut l’utiliser ainsi pour obtenir un entier de 0 à 99 :

int valeur = (int) (Math.random() * 100);

Cette écriture est correcte pour un exemple court, mais je préfère généralement une instance explicite de générateur. Elle rend la limite plus visible, facilite les tests et évite de disperser la logique aléatoire dans le code.

ThreadLocalRandom pour les applications concurrentes

Dans une application qui traite plusieurs requêtes en parallèle, ThreadLocalRandom évite qu’une même instance soit partagée inutilement entre les threads. L’appel est direct :

import java.util.concurrent.ThreadLocalRandom;

int valeur = ThreadLocalRandom.current().nextInt(10, 21);

Le résultat est compris entre 10 inclus et 21 exclu, donc entre 10 et 20. Cette classe est un bon choix pour des délais variables, des simulations ou des répartitions de charge, mais la documentation Java rappelle qu’elle n’est pas cryptographiquement sûre.

Comment choisir un nombre ou un élément sans biais

La plupart des problèmes ne viennent pas du générateur lui-même, mais d’une mauvaise transformation du résultat. Il faut toujours vérifier les bornes, le cas d’une collection vide et la possibilité de répétition.

Tirer un élément dans une liste

Pour sélectionner un élément au hasard, j’utilise l’index généré par nextInt. La taille de la liste devient naturellement la borne supérieure.

import java.util.List;
import java.util.Random;

List couleurs = List.of("bleu", "vert", "orange", "violet");
Random random = new Random();

String choix = couleurs.get(random.nextInt(couleurs.size()));

Cette méthode donne à chaque élément la même probabilité, à condition que la liste ne soit pas vide. En production, je traite donc explicitement le cas isEmpty() au lieu de laisser une exception apparaître au mauvais endroit.

Obtenir un entier dans un intervalle

Pour un intervalle de 20 à 50 inclus, la formule classique est la suivante :

int minimum = 20;
int maximum = 50;

int valeur = random.nextInt(maximum - minimum + 1) + minimum;

Avec les méthodes modernes qui acceptent une origine et une borne, on peut aussi écrire une version plus lisible :

int valeur = random.nextInt(20, 51);

Je privilégie cette deuxième forme lorsque la version du JDK utilisée par le projet la prend en charge. Elle limite les erreurs de calcul et montre immédiatement que la borne finale est exclusive.

Éviter le piège de l’opérateur modulo

Écrire random.nextInt() % 10 semble pratique, mais le résultat peut être négatif et créer une distribution inégale. Il vaut mieux demander directement une borne au générateur avec nextInt(10), qui gère cette contrainte correctement.

Le même principe vaut pour les collections. Si l’objectif est de mélanger une liste entière, Collections.shuffle est plus fiable qu’une boucle improvisée qui échange des éléments au hasard. Une bonne solution prête à l’emploi est souvent plus sûre qu’une formule supposée équivalente.

Graphique montrant le temps nécessaire pour générer 100 millions de nombres random en Java, avec une augmentation du temps avec le nombre de threads.

Random, ThreadLocalRandom ou SecureRandom

Le nom des classes peut donner l’impression qu’elles sont interchangeables. Elles ne le sont pas. J’utilise surtout le niveau de risque et le contexte d’exécution pour faire mon choix.

Pour une simulation ou un jeu

Random convient à un jeu de plateau, à une génération de terrain ou à une simulation simple. Si plusieurs calculs s’exécutent en parallèle, ThreadLocalRandom devient plus naturel, car chaque thread peut générer ses valeurs sans dépendre d’un objet partagé.

Pour des simulations scientifiques ou des calculs parallèles qui doivent rester analysables, SplittableRandom peut être intéressant. Il facilite la séparation des flux pseudo-aléatoires, mais il ne faut pas interpréter son nom comme une garantie de sécurité.

Pour un secret ou un jeton

Un code de validation, un identifiant de session ou une valeur utilisée dans un mécanisme d’authentification doit être généré avec SecureRandom.

import java.security.SecureRandom;

SecureRandom secureRandom = new SecureRandom();
int code = secureRandom.nextInt(1_000_000);

Ce code produit une valeur entre 0 et 999 999. Pour afficher toujours six chiffres, il faut compléter les zéros à gauche, car la valeur 42 doit devenir 000042. Le générateur ne s’occupe pas de cette présentation.

Je déconseille aussi de fixer une graine avec setSeed pour un secret. Une graine connue ou prévisible peut rendre toute la séquence devinable. La reproductibilité est utile dans un test, pas dans un mécanisme de sécurité.

Reproductibilité, tests et performances

Un résultat aléatoire qui change à chaque exécution est agréable dans une application, mais pénible à tester. Pour vérifier un algorithme, je préfère injecter le générateur et fournir une graine fixe dans le test.

Random random = new Random(12345L);

int premier = random.nextInt();
int second = random.nextInt();

Avec la même graine et la même suite d’appels, on obtient une séquence reproductible. Cela permet de reproduire un bug ou de comparer deux versions d’un calcul. Cette propriété ne signifie toutefois pas que les valeurs sont imprévisibles.

Éviter de recréer un générateur en boucle

Créer une nouvelle instance à chaque tour est rarement utile :

for (int i = 0; i < 1000; i++) {
    Random random = new Random();
    traiter(random.nextInt());
}

Il est plus propre de créer l’instance une seule fois et de la réutiliser, ou d’appeler ThreadLocalRandom.current() dans un contexte concurrent. Cette organisation réduit le bruit dans le code et évite des séquences inutilement proches lorsque les graines sont initialisées à partir du temps système.

Lire aussi : Int en Double Java - Évitez les erreurs de calcul!

Utiliser les flux avec discernement

Java permet aussi de produire des flux d’entiers :

Random random = new Random();

random.ints(5, 1, 101)
      .forEach(System.out::println);

Le programme génère cinq valeurs entre 1 et 100. Les flux sont élégants pour une transformation courte, mais une boucle reste souvent plus lisible lorsque le traitement comporte des conditions, des arrêts ou des contrôles d’erreur.

Les erreurs qui rendent un tirage peu fiable

  • Confondre borne incluse et borne exclue conduit à des valeurs manquantes ou à une exception.
  • Utiliser Random pour un mot de passe expose le secret à des prédictions possibles.
  • Partager un générateur sans réfléchir complique les applications fortement concurrentes.
  • Oublier une collection vide provoque une erreur au moment de l’accès à l’index.
  • Mélanger puis sélectionner avec remise ne répond pas au même besoin qu’un tirage sans répétition.

Le dernier point mérite une attention particulière. Si je dois tirer trois gagnants distincts, trois appels indépendants peuvent sélectionner deux fois la même personne. Je mélange alors la liste, puis je prends les trois premiers éléments, ou je retire chaque élément déjà choisi.

Pour un système sensible, je vérifie aussi la taille de l’espace possible. Un code à six chiffres offre 1 000 000 de combinaisons, ce qui peut être acceptable avec une durée de validité courte et une limitation des tentatives, mais insuffisant pour un secret durable.

La règle simple pour ne plus se tromper de générateur

Pour un programme pédagogique, un jeu ou une simulation ordinaire, commencez avec Random. Pour du code serveur exécuté par plusieurs threads, choisissez plutôt ThreadLocalRandom. Pour des calculs parallèles reproductibles, examinez SplittableRandom ou l’API moderne des générateurs.

Dès qu’une valeur protège un compte, une session ou une opération, utilisez SecureRandom et ajoutez des protections complémentaires comme l’expiration, la limitation des essais et le stockage prudent des données.

La vraie compétence ne consiste donc pas à produire un nombre au hasard, mais à choisir un aléatoire adapté au risque, aux performances et au comportement attendu du programme. C’est ce discernement qui transforme un petit exemple Java en code fiable.

Questions fréquentes

Random convient aux jeux, exercices et simulations simples. Pour une application exécutée par plusieurs threads, ThreadLocalRandom évite de partager inutilement une même instance. SplittableRandom peut servir aux calculs parallèles reproductibles.
La méthode nextInt(bound) inclut zéro et exclut la limite. Pour obtenir une valeur de 20 à 50 inclus, utilisez random.nextInt(20, 51) lorsque le JDK le permet, car la borne finale est 51 et reste exclusive.
Utilisez un index produit par nextInt(liste.size()), puis récupérez l’élément correspondant. Vérifiez d’abord que la liste n’est pas vide. Pour mélanger une liste entière, préférez Collections.shuffle à une boucle d’échanges improvisée.
SecureRandom est conçu pour générer des valeurs difficiles à prédire, contrairement à Random. Il convient aux jetons, codes de validation et identifiants de session. Une graine fixe rend une séquence reproductible pour les tests, mais peut la rendre devinable et ne doit pas protéger un secret.
Évaluer l'article

Moyenne: 0.0 / 5 · 0 évaluations

Tags

random threadlocalrandom securerandom splittablerandom simulations
Autor Denis Ribeiro
Denis Ribeiro
Je m'appelle Denis Ribeiro et j'ai quatre ans d'expérience dans le domaine de la technologie, en particulier dans les domaines du web, de l'intelligence artificielle, des réseaux et de la sécurité. Mon intérêt pour ces sujets a commencé dès mon plus jeune âge, lorsque j'ai découvert à quel point la technologie pouvait transformer notre quotidien. J'aime expliquer des concepts complexes de manière simple, afin d'aider mes lecteurs à mieux comprendre les enjeux actuels et futurs de notre monde numérique. Dans mes écrits, je m'efforce de fournir des informations utiles, précises et à jour. Je prends le temps de vérifier mes sources et de comparer les informations, tout en suivant les dernières tendances du secteur. Mon objectif est de rendre l'apprentissage accessible à tous, en organisant mes connaissances de manière claire et structurée, afin que chacun puisse naviguer avec confiance dans cet univers en constante évolution.
Commentaires (0)
Ajouter un commentaire