Nombres aléatoires Java - Le guide complet pour bien choisir

Denis Ribeiro .

17 mai 2026

Schéma d'un générateur de nombres pseudo-aléatoires (GNPA) : une graine initie des états internes et des fonctions de transformation pour produire un nombre pseudo-aléatoire, comme en java.
Générer des nombres aléatoires en Java semble simple au premier abord, mais le bon choix d’API change vite selon le contexte: simulation, jeu, test reproductible, traitement parallèle ou sécurité. Le sujet souvent résumé par java random number recouvre en réalité plusieurs outils natifs, avec des comportements différents sur les bornes, la qualité statistique et la sécurité. Je vais aller droit au but: quoi utiliser, comment l’utiliser correctement et quelles erreurs éviter pour obtenir un code propre et prévisible dans le bon sens du terme.

Ce qui compte vraiment, ce n’est pas seulement d’obtenir une valeur qui “a l’air” aléatoire. C’est de savoir si elle doit être reproductible, rapide, indépendante entre threads ou impossible à deviner. C’est là que Java offre des options utiles, mais qu’il faut savoir trier.

L’essentiel à garder avant de choisir un générateur

  • Random reste valide, mais je le réserve surtout aux cas simples ou au code hérité.
  • ThreadLocalRandom est le plus pratique quand plusieurs threads tirent des valeurs en parallèle.
  • RandomGenerator modernise l’API et permet de choisir un algorithme par son nom.
  • SecureRandom s’impose dès qu’un résultat doit rester imprévisible pour la sécurité.
  • Les bornes sont presque toujours inclusives au début et exclusives à la fin.
  • Évitez `nextInt() % n` quand vous avez une méthode native plus propre.

Ce qu’il faut comprendre avant de générer un nombre

En Java, un nombre “aléatoire” est presque toujours pseudo-aléatoire. Cela veut dire qu’un algorithme produit une suite qui paraît imprévisible, mais qui reste déterministe à partir d’un état initial, souvent appelé seed. C’est très bien pour la majorité des usages applicatifs, des jeux aux simulations, mais ce n’est pas le même niveau d’imprévisibilité qu’un générateur cryptographique.

Le deuxième point à intégrer, c’est la gestion des bornes. En pratique, la plupart des méthodes Java renvoient une valeur entre une borne basse incluse et une borne haute exclue. Cette nuance évite beaucoup d’erreurs de logique, surtout quand on génère des indices de tableau, des dés virtuels ou des intervalles de score.

  • Pour un entier entre 0 et 99, on utilise une borne haute de 100.
  • Pour un entier entre 1 et 6, on utilise une borne basse de 1 et une borne haute de 7.
  • Pour un `double`, la borne haute reste en général exclue, ce qui est normal dans la plupart des API modernes.

Je vois souvent des bugs qui viennent d’un simple décalage de 1. Le résultat n’est pas spectaculaire, mais il fausse le comportement du programme. Une fois ce point clair, le vrai sujet devient le choix de l’API la plus adaptée.

Titre

Quelle API Java utiliser selon le contexte

Java a plusieurs chemins valides, et je préfère les distinguer franchement plutôt que de donner une réponse unique. Le bon générateur dépend du besoin réel: vitesse, reproductibilité, concurrence ou sécurité. Voici comment je les classe dans un projet moderne.

API Atout principal Limite Quand je l’utilise
Random Simple, connu, toujours disponible Plus ancien, moins adapté aux usages modernes très concurrentiels Code hérité, petit script, besoin basique
ThreadLocalRandom Pratique en environnement multi-thread Pas pensé pour le partage entre threads ni pour le contrôle fin du seed Applications serveur, tâches parallèles, génération fréquente
SplittableRandom Très bon pour des générateurs indépendants et les traitements parallèles Moins intuitif pour un débutant Parallélisme, simulation, génération de sous-flux indépendants
RandomGenerator API moderne, choix explicite de l’algorithme Nécessite de comprendre un peu mieux le modèle Code neuf que je veux garder propre et évolutif
SecureRandom Imprévisibilité adaptée à la sécurité Plus coûteux qu’un PRNG classique Jetons, mots de passe temporaires, clés, identifiants non devinables

Dans les versions récentes du JDK, j’aime bien partir de java.util.random pour le code applicatif, puis basculer vers SecureRandom dès que l’imprévisibilité devient un critère de sécurité. Cette séparation rend le code plus lisible et évite de faire porter à un seul outil des responsabilités qu’il n’a pas.

Le point important à retenir ici est simple: ne partagez pas aveuglément un même générateur entre threads si l’API n’est pas pensée pour cela, et ne prenez pas un générateur “rapide” pour un besoin sensible. Une fois la bonne famille choisie, les exemples concrets deviennent beaucoup plus lisibles.

Des exemples concrets à réutiliser dans un projet

Je préfère montrer des cas courts et directement exploitables. En pratique, c’est souvent ce qu’on cherche: un entier dans une plage, un nombre décimal, une séquence de valeurs ou un test reproductible.

Un entier dans une plage simple

import java.util.concurrent.ThreadLocalRandom;

int valeur = ThreadLocalRandom.current().nextInt(0, 100); // 0 à 99
int de = ThreadLocalRandom.current().nextInt(1, 7);        // 1 à 6
Ici, la borne haute est exclue. C’est le comportement le plus propre pour éviter les erreurs de décalage, surtout quand on manipule des indices ou des intervalles fermés à gauche et ouverts à droite.

Un nombre décimal uniforme

import java.util.concurrent.ThreadLocalRandom;

double ratio = ThreadLocalRandom.current().nextDouble(0.0, 1.0);
double prix = ThreadLocalRandom.current().nextDouble(10.0, 20.0);

Pour un `double`, je pense en termes de plage, pas en termes d’arrondi. Si vous avez besoin d’un montant affichable, arrondissez ensuite au format métier, au lieu de trafiquer la génération elle-même.

Une suite de valeurs pour un test ou une simulation

import java.util.concurrent.ThreadLocalRandom;

ThreadLocalRandom.current()
    .ints(10, 1, 100)
    .forEach(System.out::println);

Les streams de valeurs sont utiles quand on veut alimenter un traitement, un test ou une simulation de charge. Je les utilise surtout quand le code est plus lisible en mode séquence qu’en boucle manuelle.

Lire aussi : Getters Java - Maîtrisez les bonnes pratiques et évitez les pièges

Un générateur reproductible pour les tests

import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;

RandomGeneratorFactory factory =
        RandomGeneratorFactory.of("L64X128MixRandom");

RandomGenerator rng = factory.create(42L);
int score = rng.nextInt(1, 11);

Cette approche me plaît pour les tests et les scénarios reproductibles. Avec un seed fixe, je peux rejouer exactement la même suite de valeurs, ce qui simplifie énormément le diagnostic quand un test échoue de manière intermittente.

Le vrai avantage de ces exemples, c’est qu’ils montrent le bon réflexe: choisir la méthode selon l’usage, puis laisser Java gérer la distribution à votre place. Et c’est justement là que les erreurs classiques commencent à disparaître.

Les erreurs qui donnent une fausse impression de hasard

Je vois toujours les mêmes pièges, et ils coûtent du temps parce qu’ils donnent des résultats “presque corrects”. Le problème, c’est que le code semble marcher jusqu’au jour où il ne marche plus, ou pire, jusqu’au moment où il produit un comportement biaisé.

  1. Utiliser `nextInt() % n` : le modulo peut introduire un biais de distribution. La méthode native avec borne est préférable.
  2. Oublier que la borne haute est exclue : c’est l’erreur la plus fréquente sur les indices et les dés virtuels.
  3. Recréer un générateur à chaque appel : cela alourdit le code et peut produire des suites moins utiles que prévu.
  4. Partager n’importe quel générateur entre threads : dans un code parallèle, il faut respecter les recommandations de l’API.
  5. Confondre “aléatoire” et “imprévisible” : pour la sécurité, un PRNG classique n’est pas le bon outil.

Je recommande aussi de tester les cas limites: borne 0, borne négative, intervalle inversé, valeur maximale du type. Ce sont précisément les situations où les erreurs de génération apparaissent le plus vite. Quand ces points sont maîtrisés, le passage à un générateur sécurisé devient beaucoup plus clair.

Quand passer à SecureRandom sans hésiter

Dès qu’un nombre ou une séquence de bytes sert à protéger quelque chose, je sors du registre “pseudo-aléatoire classique”. C’est le cas pour un jeton de réinitialisation, un mot de passe temporaire, un identifiant de session, une clé ou tout mécanisme qu’un attaquant pourrait essayer de deviner.

import java.security.SecureRandom;
import java.util.HexFormat;

SecureRandom secureRandom = new SecureRandom();
byte[] token = new byte[32];
secureRandom.nextBytes(token);

String tokenHex = HexFormat.of().formatHex(token);

Avec 32 octets, on obtient déjà un jeton de 256 bits avant encodage, ce qui est un ordre de grandeur courant pour des usages sérieux. Je préfère cette taille parce qu’elle laisse de la marge sans complexifier le code. Et contrairement à un générateur généraliste, SecureRandom est conçu pour rester imprévisible, y compris dans des contextes sensibles.

Le compromis est simple: c’est plus coûteux qu’un générateur orienté performance, mais c’est le bon prix quand la sécurité dépend du résultat. Pour une simulation de jeu, c’est excessif; pour un token d’accès, c’est la base.

Le réflexe que je garderais en 2026

Si je devais simplifier au maximum, je garderais cette règle de décision. Pour du code applicatif standard, je partirais sur RandomGenerator ou ThreadLocalRandom selon le contexte d’exécution. Pour du parallélisme propre, je regarderais SplittableRandom ou une implémentation SplittableGenerator. Pour tout ce qui touche à la sécurité, je passerais directement à SecureRandom.

  • Besoin simple et local : générateur classique avec bornes explicites.
  • Besoin multi-thread : générateur par thread, pas un objet partagé par habitude.
  • Besoin reproductible : seed fixe et instance conservée, surtout pour les tests.
  • Besoin sensible : aucune improvisation, seulement un générateur cryptographiquement fort.

En pratique, le meilleur code est souvent celui qui dit clairement ce qu’il veut faire: produire une valeur dans une plage, répéter une expérience, ou empêcher toute prédiction. C’est exactement là que Java est solide, à condition de choisir la bonne API dès le départ.

Questions fréquentes

En Java, presque tous les nombres sont pseudo-aléatoires, générés par un algorithme déterministe à partir d'une "seed". Un vrai nombre aléatoire est imprévisible, souvent utilisé pour la sécurité. Les PRNG sont suffisants pour la plupart des simulations et jeux.
Utilisez ThreadLocalRandom dans un environnement multi-thread pour une meilleure performance et éviter la contention. Random est plus ancien et moins optimisé pour les usages concurrents, mais reste valable pour des cas simples ou du code hérité.
SecureRandom est plus lent car il utilise des sources d'entropie du système d'exploitation pour générer des nombres cryptographiquement forts et imprévisibles. Ce processus est plus coûteux en ressources que les algorithmes pseudo-aléatoires classiques, mais essentiel pour la sécurité.
Pour la reproductibilité, utilisez un générateur comme RandomGenerator avec une "seed" fixe (par exemple, `RandomGeneratorFactory.of("L64X128MixRandom").create(42L)`). Cela garantit que la même séquence de nombres sera générée à chaque exécution, facilitant le débogage et les tests.

Évaluer l'article

Moyenne: 0.0 / 5 · 0 évaluations

Tags

java random number générer nombre aléatoire java threadlocalrandom utilisation securerandom quand l'utiliser
Autor Denis Ribeiro
Denis Ribeiro
Je m'appelle Denis Ribeiro et je suis passionné par les technologies, en particulier dans les domaines du web, de l'intelligence artificielle, des réseaux et de la sécurité. Fort de plusieurs années d'expérience en tant qu'analyste de l'industrie, j'ai eu l'occasion d'explorer en profondeur ces sujets, en me concentrant sur les évolutions et les tendances qui façonnent notre monde numérique. Mon expertise me permet d'analyser des données complexes et de les présenter de manière accessible, afin que chacun puisse comprendre les enjeux technologiques actuels. Je m'efforce d'apporter une perspective objective et factuelle à mes écrits, en vérifiant rigoureusement les informations pour garantir leur fiabilité. Je suis engagé à fournir à mes lecteurs des contenus précis, à jour et impartiaux, car je crois fermement que l'accès à une information de qualité est essentiel pour naviguer dans l'univers technologique en constante évolution. Mon objectif est de contribuer à une meilleure compréhension des défis et des opportunités que présente le monde numérique.

Commentaires (0)

Ajouter un commentaire