Table of Contents
Generer des Nombres Aleatoires en Java : Guide Complet
Introduction
La generation de nombres aleatoires est une fonctionnalite fondamentale dans de nombreuses applications Java. Que ce soit pour creer des jeux video, effectuer des simulations statistiques, generer des identifiants uniques, ou implementer des mecanismes de securite cryptographique, comprendre comment generer des nombres aleatoires de maniere appropriee est essentiel pour tout developpeur Java.
Java offre plusieurs approches pour generer des nombres aleatoires, chacune adaptee a des cas d’utilisation specifiques. Dans cet article, nous explorerons en profondeur les trois principales classes de generation de nombres aleatoires : Random, ThreadLocalRandom et SecureRandom. Nous verrons egalement comment choisir la bonne classe selon vos besoins et comment eviter les erreurs courantes.
Les Differentes Classes de Generation
La Classe Random : L’Approche Classique
La classe java.util.Random est la solution la plus ancienne et la plus simple pour generer des nombres pseudo-aleatoires en Java. Elle utilise un algorithme de congruence lineaire qui produit une sequence de nombres qui semble aleatoire.
import java.util.Random;
public class RandomBasicExample {
public static void main(String[] args) {
// Creation d'une instance Random
Random random = new Random();
// Differents types de nombres aleatoires
int randInt = random.nextInt(); // Entier quelconque
int randIntBound = random.nextInt(100); // Entier entre 0 et 99
long randLong = random.nextLong(); // Long quelconque
double randDouble = random.nextDouble(); // Double entre 0.0 et 1.0
float randFloat = random.nextFloat(); // Float entre 0.0 et 1.0
boolean randBool = random.nextBoolean(); // true ou false
System.out.println("Entier aleatoire: " + randInt);
System.out.println("Entier borne [0-99]: " + randIntBound);
System.out.println("Long aleatoire: " + randLong);
System.out.println("Double aleatoire: " + randDouble);
System.out.println("Float aleatoire: " + randFloat);
System.out.println("Boolean aleatoire: " + randBool);
}
}
Utilisation d’une Seed pour la Reproductibilite
Une caracteristique importante de Random est la possibilite d’utiliser une “seed” (graine) pour reproduire exactement la meme sequence de nombres :
import java.util.Random;
public class RandomSeedExample {
public static void main(String[] args) {
// Meme seed = meme sequence
Random random1 = new Random(12345);
Random random2 = new Random(12345);
System.out.println("Sequence 1:");
for (int i = 0; i < 5; i++) {
System.out.print(random1.nextInt(100) + " ");
}
System.out.println("\nSequence 2 (identique):");
for (int i = 0; i < 5; i++) {
System.out.print(random2.nextInt(100) + " ");
}
// Les deux sequences seront identiques !
}
}
La Classe ThreadLocalRandom : Performance en Multithreading
Introduite dans Java 7, ThreadLocalRandom est optimisee pour les environnements multithread. Contrairement a Random, elle ne souffre pas de contention entre threads.
import java.util.concurrent.ThreadLocalRandom;
public class ThreadLocalRandomExample {
public static void main(String[] args) {
// Pas de new, on utilise current()
ThreadLocalRandom tlr = ThreadLocalRandom.current();
// Generation de nombres dans une plage
int randInt = tlr.nextInt(1, 101); // Entre 1 et 100 inclus
long randLong = tlr.nextLong(1000, 10000); // Entre 1000 et 9999
double randDouble = tlr.nextDouble(0.0, 1.0);
System.out.println("Entier [1-100]: " + randInt);
System.out.println("Long [1000-9999]: " + randLong);
System.out.println("Double [0.0-1.0]: " + randDouble);
}
}
Exemple d’Utilisation en Contexte Multithread
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ThreadLocalRandomMultithreadExample {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
executor.submit(() -> {
// Chaque thread utilise sa propre instance
int random = ThreadLocalRandom.current().nextInt(1000);
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " -> " + random);
});
}
executor.shutdown();
executor.awaitTermination(5, TimeUnit.SECONDS);
}
}
La Classe SecureRandom : Securite Cryptographique
Pour les applications necessitant une securite forte (generation de cles, tokens, mots de passe), SecureRandom est la seule option acceptable.
import java.security.SecureRandom;
import java.util.Base64;
public class SecureRandomExample {
public static void main(String[] args) {
SecureRandom secureRandom = new SecureRandom();
// Generation de bytes aleatoires (pour cles, tokens, etc.)
byte[] randomBytes = new byte[32];
secureRandom.nextBytes(randomBytes);
// Conversion en Base64 pour affichage
String token = Base64.getEncoder().encodeToString(randomBytes);
System.out.println("Token securise: " + token);
// Generation d'un entier securise
int secureInt = secureRandom.nextInt(1000000);
System.out.println("Code OTP: " + String.format("%06d", secureInt % 1000000));
}
}
Generation de Mots de Passe Securises
import java.security.SecureRandom;
public class SecurePasswordGenerator {
private static final String CHARS =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*";
public static String generatePassword(int length) {
SecureRandom random = new SecureRandom();
StringBuilder password = new StringBuilder(length);
for (int i = 0; i < length; i++) {
int index = random.nextInt(CHARS.length());
password.append(CHARS.charAt(index));
}
return password.toString();
}
public static void main(String[] args) {
System.out.println("Mot de passe genere: " + generatePassword(16));
}
}
Generation dans une Plage Specifique
Plage avec Borne Inferieure et Superieure
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class RangeExample {
// Methode classique avec Random
public static int randomInRange(Random random, int min, int max) {
return random.nextInt(max - min + 1) + min;
}
public static void main(String[] args) {
Random random = new Random();
// Generer un nombre entre 50 et 100 (inclus)
int result = randomInRange(random, 50, 100);
System.out.println("Random classique [50-100]: " + result);
// Avec ThreadLocalRandom (plus simple)
int tlrResult = ThreadLocalRandom.current().nextInt(50, 101);
System.out.println("ThreadLocalRandom [50-100]: " + tlrResult);
}
}
Generation de Nombres Sans Doublons
Voici une implementation complete et optimisee pour generer des nombres aleatoires sans doublons :
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
public class UniqueRandomNumbers {
// Methode avec Collections.shuffle (recommandee)
public static List<Integer> getUniqueRandomsShuffle(int min, int max, int count) {
if (count > (max - min + 1)) {
throw new IllegalArgumentException("Count exceeds available range");
}
List<Integer> numbers = new ArrayList<>();
for (int i = min; i <= max; i++) {
numbers.add(i);
}
Collections.shuffle(numbers);
return numbers.subList(0, count);
}
// Methode avec Set (pour grandes plages)
public static Set<Integer> getUniqueRandomsSet(int min, int max, int count) {
Set<Integer> uniqueNumbers = new LinkedHashSet<>();
ThreadLocalRandom random = ThreadLocalRandom.current();
while (uniqueNumbers.size() < count) {
uniqueNumbers.add(random.nextInt(min, max + 1));
}
return uniqueNumbers;
}
public static void main(String[] args) {
// 6 nombres uniques entre 1 et 49 (comme au Loto)
List<Integer> lotoNumbers = getUniqueRandomsShuffle(1, 49, 6);
Collections.sort(lotoNumbers);
System.out.println("Numeros Loto: " + lotoNumbers);
// 10 nombres uniques entre 1 et 1000
Set<Integer> uniqueSet = getUniqueRandomsSet(1, 1000, 10);
System.out.println("10 nombres uniques: " + uniqueSet);
}
}
Utilisation avec les Streams Java 8+
Java 8 a introduit des methodes de generation de streams aleatoires :
import java.util.Random;
import java.util.stream.IntStream;
import java.util.concurrent.ThreadLocalRandom;
public class RandomStreamsExample {
public static void main(String[] args) {
Random random = new Random();
// Stream infini de nombres aleatoires
System.out.println("5 entiers aleatoires:");
random.ints()
.limit(5)
.forEach(System.out::println);
// Stream borne
System.out.println("\n5 entiers entre 1 et 100:");
random.ints(5, 1, 101)
.forEach(System.out::println);
// Avec ThreadLocalRandom
System.out.println("\nSomme de 1000 nombres aleatoires:");
long sum = ThreadLocalRandom.current()
.ints(1000, 0, 100)
.sum();
System.out.println("Somme: " + sum);
// Statistiques sur les nombres generes
System.out.println("\nStatistiques sur 10000 nombres:");
IntStream.generate(() -> ThreadLocalRandom.current().nextInt(100))
.limit(10000)
.summaryStatistics();
}
}
Bonnes Pratiques
1. Choisir la Bonne Classe
| Cas d’utilisation | Classe recommandee |
|---|---|
| Usage general, simple | Random |
| Applications multithread | ThreadLocalRandom |
| Securite, cryptographie | SecureRandom |
| Tests reproductibles | Random avec seed |
2. Reutiliser les Instances
// MAUVAIS : Creer une nouvelle instance a chaque appel
public int badRandom() {
return new Random().nextInt(100); // Ne pas faire ca !
}
// BON : Reutiliser une instance
private static final Random RANDOM = new Random();
public int goodRandom() {
return RANDOM.nextInt(100);
}
// MEILLEUR en multithread : ThreadLocalRandom
public int bestRandom() {
return ThreadLocalRandom.current().nextInt(100);
}
3. Ne Jamais Utiliser Math.random() pour la Securite
// DANGEREUX pour la securite !
double unsafeRandom = Math.random();
// SECURISE
SecureRandom secureRandom = new SecureRandom();
double safeRandom = secureRandom.nextDouble();
4. Initialiser SecureRandom Correctement
// Laisser le systeme choisir le meilleur algorithme
SecureRandom secure = new SecureRandom();
// Ou specifier explicitement (si necessaire)
SecureRandom secureSha1 = SecureRandom.getInstance("SHA1PRNG");
Pieges Courants a Eviter
1. Biais dans la Generation avec Modulo
// MAUVAIS : Biais subtil avec modulo
int biased = Math.abs(random.nextInt()) % 100;
// BON : Utiliser nextInt(bound)
int unbiased = random.nextInt(100);
2. Seed Previsible
// MAUVAIS : Seed base sur le temps (previsible)
Random predictable = new Random(System.currentTimeMillis());
// BON : Laisser Java choisir la seed
Random unpredictable = new Random();
// MEILLEUR pour la securite : SecureRandom
SecureRandom secure = new SecureRandom();
3. Partage de Random entre Threads
// MAUVAIS : Contention et problemes de performance
private static final Random SHARED = new Random();
// Utilise par plusieurs threads...
// BON : ThreadLocalRandom pour le multithreading
private int getRandomValue() {
return ThreadLocalRandom.current().nextInt(100);
}
4. Oublier les Bornes Exclusives
// nextInt(100) retourne [0, 99], pas [0, 100] !
int zeroTo99 = random.nextInt(100); // 100 est EXCLU
// Pour inclure 100 :
int zeroTo100 = random.nextInt(101); // Maintenant 100 est inclus
5. Utiliser Random pour des Applications Sensibles
// JAMAIS pour la generation de tokens, cles, ou mots de passe !
String unsafeToken = String.valueOf(new Random().nextLong());
// TOUJOURS utiliser SecureRandom
SecureRandom secure = new SecureRandom();
byte[] tokenBytes = new byte[32];
secure.nextBytes(tokenBytes);
String safeToken = Base64.getEncoder().encodeToString(tokenBytes);
Comparaison des Performances
import java.security.SecureRandom;
import java.util.Random;
import java.util.concurrent.ThreadLocalRandom;
public class PerformanceComparison {
private static final int ITERATIONS = 10_000_000;
public static void main(String[] args) {
// Random
Random random = new Random();
long start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
random.nextInt(100);
}
long randomTime = System.nanoTime() - start;
// ThreadLocalRandom
start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
ThreadLocalRandom.current().nextInt(100);
}
long tlrTime = System.nanoTime() - start;
// SecureRandom
SecureRandom secure = new SecureRandom();
start = System.nanoTime();
for (int i = 0; i < ITERATIONS; i++) {
secure.nextInt(100);
}
long secureTime = System.nanoTime() - start;
System.out.printf("Random: %d ms%n", randomTime / 1_000_000);
System.out.printf("ThreadLocalRandom: %d ms%n", tlrTime / 1_000_000);
System.out.printf("SecureRandom: %d ms%n", secureTime / 1_000_000);
}
}
Conclusion
La generation de nombres aleatoires en Java offre plusieurs options adaptees a differents besoins :
Random: Simple et efficace pour les cas d’utilisation generaux sans contraintes de securite ou de multithreading intensif.ThreadLocalRandom: Le choix ideal pour les applications multithread grace a ses performances superieures et son absence de contention.SecureRandom: Indispensable pour toute application necessitant une securite cryptographique (tokens, mots de passe, cles).
Choisissez toujours la classe appropriee en fonction de vos besoins specifiques. N’utilisez jamais Random ou Math.random() pour des fonctionnalites liees a la securite, et privilegiez ThreadLocalRandom dans les environnements concurrents pour des performances optimales.
Ressources Complementaires
In-Article Ad
Dev Mode
Tags
Mahmoud DEVO
Senior Full-Stack Developer
I'm a passionate full-stack developer with 10+ years of experience building scalable web applications. I write about Vue.js, Node.js, PostgreSQL, and modern DevOps practices.
Enjoyed this article?
Subscribe to get more tech content delivered to your inbox.
Related Articles
Synchronisation Java avec AtomicInteger : eviter la contention et optimiser les performances
Decouvrez comment utiliser les types atomiques Java (AtomicInteger, AtomicLong, AtomicReference, AtomicBoolean) pour reduire la contention, eviter les blocages et ameliorer les performances.
Gestion des interruptions dans les tâches Java : techniques
Voici une proposition de meta description : "Apprenez à gérer les interruptions dans vos programmes Java avec des exemples concrets et des conseils pratiques.
Gestion des Exceptions en Java : Evitez les Pieges Courants et les Blocages
Guide complet sur la gestion des exceptions en Java. Apprenez a eviter les deadlocks, gerer les interruptions de threads, utiliser correctement les stacktraces et maitriser les bonnes pratiques de programmation defensive.