最推荐使用fisher-yates洗牌算法(如collections.shuffle()或手动实现),时间复杂度o(n),保证每种排列等概率;对基本类型需手动实现,核心是倒序遍历并随机交换[0,i]范围内元素。

Java 中实现数组元素随机打乱(洗牌),最推荐、最可靠的方式是使用 Fisher-Yates 洗牌算法(也叫 Knuth 洗牌),它时间复杂度 O(n),且能保证每种排列出现概率完全相等,避免了简单随机交换带来的偏差。
用 Collections.shuffle()(适用于对象数组)
如果数组元素是引用类型(如 String、Integer 等包装类或自定义对象),可先转为 List,再调用 Collections.shuffle() —— 它内部正是基于 Fisher-Yates 实现的:
- 创建 ArrayList 包装原数组
- 调用
Collections.shuffle(list) - 如有需要,再写回原数组
示例:
String[] arr = {"A", "B", "C", "D"};
List<string> list = Arrays.asList(arr);
Collections.shuffle(list); // 已打乱
// arr 现在也被修改(因为 asList 返回的是原数组的视图)
</string>
⚠️ 注意:Arrays.asList() 对基本类型数组(如 int[])无效,且返回的 List 不支持 add/remove;若需操作副本,可用 new ArrayList(Arrays.asList(...))。
手动实现 Fisher-Yates(兼容所有数组类型)
对基本类型数组(int[]、double[] 等)或需要完全控制逻辑时,应手写 Fisher-Yates。核心思想:从后往前遍历,每次随机选一个位置(含当前位)与当前位交换:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 索引 i 从
arr.length - 1递减到 1 - 生成随机索引 j ∈ [0, i]
- 交换
arr[i]和arr[j]
示例(int 数组):
Random rand = new Random();
int[] arr = {1, 2, 3, 4, 5};
for (int i = arr.length - 1; i > 0; i--) {
int j = rand.nextInt(i + 1); // [0, i]
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
避免常见错误
以下做法看似随机,但会导致分布不均(某些排列概率更高):
- 对每个位置 i 随机选 j ∈ [0, n),然后 swap(arr[i], arr[j]) —— 这会产生 nⁿ 种可能交换,但只有 n! 种合法排列,无法整除,必然不均匀
- 用
Math.random()而不加 1 或边界处理,导致nextInt(i)实际范围是 [0, i),漏掉 i 自身 - 用
new Random(seed)在循环内反复新建 Random 实例 —— 可能因时间种子相同导致重复序列
封装成通用工具方法
为提升复用性,可封装支持泛型的洗牌方法(仅限引用类型):
public static <t> void shuffle(T[] arr) {
Random rand = new Random();
for (int i = arr.length - 1; i > 0; i--) {
int j = rand.nextInt(i + 1);
T temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
</t>
调用:shuffle(new String[]{"a","b","c"});
不复杂但容易忽略细节,关键是坚持 Fisher-Yates 的“从后往前 + 范围包含当前位”逻辑,就能得到真正均匀的随机排列。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










