上一篇
在Java中,可通过以下方法避免随机数重复:,1. 使用
Collections.shuffle()打乱有序序列(如1-100),2. 使用
Set集合存储并校验重复值,3. 使用
ThreadLocalRandom或
SecureRandom配合循环检测,4. 利用数据库自增ID或UUID保证唯一性,需根据场景选择合适方案,如抽奖推荐洗牌算法,ID生成推荐UUID。
基于集合的洗牌算法(小范围高效)
原理:预生成所有可能值,随机打乱顺序后顺序取出
适用场景:有限且范围小的数据集(如1-100的抽奖)
List<Integer> numbers = new ArrayList<>();
for (int i = 1; i <= 100; i++) { // 预填充数据
numbers.add(i);
}
Collections.shuffle(numbers); // 随机打乱
// 按序取出不重复值
for (int num : numbers) {
System.out.println(num);
}
优点:O(n)时间复杂度,绝对不重复
缺点:内存占用高,不适用大范围(如10亿)
HashSet去重(动态生成)
原理:生成随机数后存入Set自动去重
Set<Integer> uniqueSet = new HashSet<>();
Random rand = new Random();
while (uniqueSet.size() < 50) { // 目标50个不重复数
int num = rand.nextInt(1000); // 范围[0,999]
uniqueSet.add(num);
}
注意:

- 集合大小接近范围上限时,性能急剧下降(碰撞概率高)
- 需设置最大循环次数避免死循环
线性同余生成器(LCG)定制
原理:通过数学公式控制随机序列不重复
// 自定义LCG参数 (a, c, m需符合Hull-Dobell定理)
class CustomLCG {
private long seed;
private final long a = 1664525, c = 1013904223, m = (long) Math.pow(2, 32);
public CustomLCG(long seed) {
this.seed = seed;
}
public int nextUnique() {
seed = (a * seed + c) % m; // 生成下一个数
return (int) (seed % 10000); // 限制范围
}
}
// 使用示例
CustomLCG generator = new CustomLCG(System.currentTimeMillis());
Set<Integer> results = new HashSet<>();
while (results.size() < 1000) {
results.add(generator.nextUnique());
}
适用场景:需要可预测序列的场景(如游戏关卡生成)
风险:参数选择不当会导致周期缩短
安全随机数(高安全性场景)
原理:用SecureRandom生成密码学强度的随机数

SecureRandom secureRandom = new SecureRandom();
Set<Integer> secureSet = new HashSet<>();
while (secureSet.size() < 10) {
int num = secureRandom.nextInt(100_000);
secureSet.add(num);
}
优势:抵御预测攻击,适用于令牌、密钥生成
代价:性能比Random低10倍以上
位图法(BitMap)超大范围优化
原理:用比特位标记已生成数字,节省内存
BitSet bitSet = new BitSet(1_000_000); // 100万范围
Random rand = new Random();
int count = 0;
while (count < 10_000) { // 目标1万个不重复数
int num = rand.nextInt(1_000_000);
if (!bitSet.get(num)) {
bitSet.set(num); // 标记已使用
count++;
}
}
优势:内存压缩至传统数组的1/32
适用:范围大但样本稀疏的场景(如百万中取1万)
关键注意事项
- 性能陷阱
HashSet去重在覆盖率>70%时碰撞率飙升,应改用洗牌或BitMap

- 随机性质量
- 普通
Random类周期为2⁴⁸,优先用ThreadLocalRandom(Java7+)
- 普通
- 并发场景
- 多线程下使用
SplittableRandom(Java8+)避免竞争
- 多线程下使用
- 分布式系统
需结合数据库唯一约束或Snowflake算法,单机随机无法保证全局唯一
方法选型建议
| 场景 | 推荐方法 |
|---|---|
| 小范围全量采样(<10⁶) | 洗牌算法(Collections.shuffle) |
| 大范围稀疏采样 | BitMap位图法 |
| 安全敏感场景 | SecureRandom + HashSet |
| 可预测序列需求 | 自定义LCG |
引用说明:
- LCG参数选择参考《计算机程序设计艺术》卷2(Donald Knuth)
- 安全随机数标准遵循NIST SP 800-90A Rev.1
- 并发随机数生成器设计基于Java官方文档
通过科学选择算法,可平衡性能、内存与安全性需求,实际开发中需严格测试边界条件(如范围溢出、线程竞争),避免业务逻辑破绽。
