Collections.shuffle 默认以 System.nanoTime() 为种子,结果不可重现;需可复现时应传入固定种子的 Random 实例,如 Collections.shuffle(list, new Random(12345L)),其基于 Fisher–Yates 算法,保证均匀打乱。

Java 中 Collections.shuffle 默认使用当前时间纳秒作为种子创建 Random 实例,因此每次调用结果不可重现;若需可复现的随机打乱(如测试、调试或分布式环境一致性场景),必须显式传入带固定种子的 Random 对象。
默认 shuffle 的随机性来源
调用 Collections.shuffle(list) 时,内部等价于:
new Random().nextInt(n)
该 Random 实例以 System.nanoTime() 为种子,毫秒级并发下可能重复,且无法跨 JVM 或跨运行复现结果。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
用指定种子实现可重现打乱
传入带固定种子的 Random 实例即可保证相同输入 List 总得到相同顺序:
- 使用
Collections.shuffle(list, new Random(12345L)) - 种子值任意 long 类型,建议用有意义的常量或配置项(如测试用例编号)
- 同一种子 + 同一原始 List + 同一 JDK 版本 → 打乱结果完全一致
算法本质:Fisher–Yates 洗牌(原地版本)
Collections.shuffle 实际采用优化的 Fisher–Yates 算法(也称 Knuth Shuffle),时间复杂度 O(n),均匀分布:
- 从列表末尾开始,对每个位置 i(i 从 list.size()-1 递减到 1),随机选一个索引 j ∈ [0, i]
- 交换 list[i] 和 list[j]
- 该过程不依赖外部随机源质量,但依赖
Random.nextInt(bound)的均匀性
注意事项与常见误区
- 传入的
Random实例可复用,无需每次新建(避免无谓对象开销) - 不要用
Math.random()或ThreadLocalRandom.current()替代 —— 它们不支持设种子 - JDK 17+ 中
Random默认使用 LXM 算法,但shuffle行为兼容;若需更强随机性(如密码学场景),应自行实现并用SecureRandom,但注意其性能开销大且不保证 shuffle 均匀性(因SecureRandom.nextInt()实现差异)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










