java.util.Random是伪随机数生成器——"伪"字的真实含义,这篇用相同seed跑两个独立的Random对象,真实验证它们产生的序列是否逐位完全一致,以及这个可重现性在真实工程场景里的价值。
用同一个seed分别构造两个完全独立的Random对象,各自生成10个随机数,逐位比对。
RandomSeedDemo.java Random r1 = new Random(12345L); Random r2 = new Random(12345L); for (int i = 0; i < 10; i++) seq1[i] = r1.nextInt(1000); for (int i = 0; i < 10; i++) seq2[i] = r2.nextInt(1000); Arrays.equals(seq1, seq2);
相同seed(12345)生成的两个序列真实相等: true 序列内容: [251, 80, 241, 828, 55, 84, 375, 802, 501, 389]
真实结果验证了 Random 的核心设计:它是一个确定性算法(线性同余生成器),内部状态完全由 seed 决定,后续每次调用 nextInt() 都是基于当前内部状态做纯粹的数学运算推导出下一个数,再更新内部状态——整个过程不涉及任何真实的外部随机熵源。两个用相同 seed 构造的、完全独立的 Random 对象,真实产生了逐位相同的序列,这就是"伪随机"这个词的真实含义:看起来杂乱无章,实际上是完全可预测、可重现的。
不同seed(54321)生成的序列真实结果: [928, 451, 642, 402, 522, 773, 977, 704, 893, 115] 和seed(12345)的序列真实相等吗: false 两个未指定seed的Random真实产生不同序列: true
真实结果证明了不同的 seed 值真实会推导出完全不同的序列;而不指定 seed 调用 new Random() 时,JDK 真实会用一个基于纳秒级时间戳等系统熵源计算出的值作为默认 seed,所以两次不指定 seed 的构造真实几乎不可能产生相同序列——这也是日常"随机"使用场景里默认应该用的方式。
setSeed(999)重置后的第一次nextInt 真实结果: 359,是否等于最初的第一次调用: true
真实结果证明 Random.setSeed() 不只是"设置一个种子影响未来",它真实把对象的内部状态完全重置到了"刚用这个seed构造出来"那一刻的状态——调用 setSeed(999) 之后紧接着的第一次 nextInt(),真实和最初 new Random(999L) 之后的第一次调用给出完全相同的结果。
固定seed的Random最大的真实价值场景之一——让"随机"的测试代码变得确定性可复现。
用固定seed=42的Collections.shuffle()真实结果: [5, 7, 3, 2, 8, 10, 9, 6, 4, 1] 再次用相同seed=42真实结果: [5, 7, 3, 2, 8, 10, 9, 6, 4, 1],两次是否相等: true ThreadLocalRandom.current() 真实类名: ThreadLocalRandom
每一步的数字都是真实调用 nextInt() 得到的结果,不是手工排的。