Implementation Methods and Principle Analysis of Generating Unique Random Numbers in Java

Nov 23, 2025 · Programming · 7 views · 7.8

Keywords: Java Random Numbers | Unique Random Numbers | Collections.shuffle | ArrayList | Fisher-Yates Algorithm

Abstract: This paper provides an in-depth exploration of various implementation methods for generating unique random numbers in Java, with a focus on the core algorithm based on ArrayList and Collections.shuffle(). It also introduces alternative solutions using Stream API in Java 8+. The article elaborates on the principles of random number generation, performance considerations, and practical application scenarios, offering comprehensive code examples and step-by-step analysis to help developers fully understand solutions to this common programming challenge.

Problem Background and Requirement Analysis

In software development, there is often a need to generate sequences of unique random numbers. This requirement is particularly common in scenarios such as lottery systems, sampling surveys, and game development. Users typically expect the generated random numbers to not only possess randomness but also ensure no duplicate values appear in specific sequences.

Basic Implementation Method

The most direct and effective solution utilizes the ArrayList from Java's collection framework and the Collections utility class. The core concept of this method is:

  1. First create a list containing all possible numerical values
  2. Use the Collections.shuffle() method to randomly rearrange the list
  3. Retrieve the required number of elements in sequence from the rearranged list

Below is a complete implementation example:

import java.util.ArrayList;
import java.util.Collections;

public class UniqueRandomGenerator {
    public static void main(String[] args) {
        // Create a list of numerical range
        ArrayList<Integer> numberList = new ArrayList<>();
        for (int i = 0; i < 100; i++) {
            numberList.add(i);
        }
        
        // Randomly rearrange list elements
        Collections.shuffle(numberList);
        
        // Retrieve first 5 unique random numbers
        System.out.println("Generated unique random numbers:");
        for (int i = 0; i < 5; i++) {
            System.out.print(numberList.get(i) + " ");
        }
    }
}

In-depth Algorithm Principle Analysis

Working Principle of Collections.shuffle()

The Collections.shuffle() method internally employs the Fisher-Yates shuffle algorithm, which has a time complexity of O(n) and can efficiently achieve random rearrangement of list elements. The algorithm steps are as follows:

// Simplified shuffle algorithm principle
for (int i = list.size() - 1; i > 0; i--) {
    int j = random.nextInt(i + 1);
    // Swap elements at positions i and j
    Collections.swap(list, i, j);
}

Random Number Seeds and Determinism

Java's Random class uses system time as the default seed, meaning that Random instances created within the same millisecond will produce the same sequence of random numbers. Therefore, in practical applications, Random instances should be reused rather than frequently creating new ones.

Alternative Solutions in Java 8+

For Java 8 and later versions, Stream API can be used to provide a more concise implementation:

import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.IntStream;

public class StreamRandomGenerator {
    public static void main(String[] args) {
        ThreadLocalRandom.current()
            .ints(0, 100)          // Generate random number stream from 0-99
            .distinct()            // Ensure uniqueness by removing duplicates
            .limit(5)              // Limit quantity to 5
            .forEach(System.out::println);
    }
}

Performance Analysis and Optimization Recommendations

Time Complexity Comparison

Memory Usage Considerations

The ArrayList method requires storing the entire numerical range, which can consume significant memory when the range is large (e.g., 1-1000000). In such cases, consider using bitmaps or other compressed data structures to optimize memory usage.

Extended Practical Application Scenarios

Lottery System Implementation

public class LotterySystem {
    private static final int MAX_NUMBER = 40;
    private static final int DRAW_COUNT = 6;
    
    public List<Integer> drawNumbers() {
        List<Integer> pool = new ArrayList<>();
        for (int i = 1; i <= MAX_NUMBER; i++) {
            pool.add(i);
        }
        Collections.shuffle(pool);
        return pool.subList(0, DRAW_COUNT);
    }
}

Test Data Generation

In unit testing, there is often a need to generate unique test IDs or sample data. This method ensures the independence and repeatability of test data.

Best Practices Summary

By deeply understanding the principles and characteristics of these implementation methods, developers can select the most appropriate solution according to specific scenarios, ensuring program performance and correctness.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.