这个方法是在看原型模式的示例代码时用到的,觉得挺有意思的,研究一下~
通过查看 JDK 文档可以看到这个方法的作用是使用(默认或指定)随机源对指定列表进行置换。
这里通过代码来简单使用一下~
代码语言:javascript复制
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;
import java.util.RandomAccess;
/**
* @author lixiaojin
* @date 2021/11/17 20:53
*/
public class TestShuffle {
private static Random r;
private static final int SHUFFLE_THRESHOLD = 5;
/**
* 使用默认的随机源随机排列指定的列表。
* @param list
*/
public static void shuffle(List<?> list) {
Random rnd = r;
if (rnd == null) {
r = rnd = new Random();
}
shuffle(list, rnd);
}
/**
* 使用指定的随机源随机排列指定的列表。
*
* 当 List 长度小于 SHUFFLE_THRESHOLD(定义为5)或者是 RandomAccess 的实例时,直接以 List 的数据结构进行打乱,否则转为数组再打乱,最后转储回 List。
*
* @param list
* @param rnd
*/
public static void shuffle(List<?> list, Random rnd) {
int size = list.size();
//
if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
for (int i = size; i > 1; i--) {
swap(list, i - 1, rnd.nextInt(i));
}
} else {
Object arr[] = list.toArray();
// Shuffle array
for (int i = size; i > 1; i--) {
swap(arr, i - 1, rnd.nextInt(i));
}
// Dump array back into list
// instead of using a raw type here, it's possible to capture
// the wildcard but it will require a call to a supplementary
// private method
ListIterator it = list.listIterator();
for (int i = 0; i < arr.length; i ) {
it.next();
it.set(arr[i]);
}
}
}
/**
* 在指定列表中的指定位置交换元素。(如果指定的位置相等,则调用此方法会使列表保持不变。)
*/
public static void swap(List<?> list, int i, int j) {
// instead of using a raw type here, it's possible to capture
// the wildcard but it will require a call to a supplementary
// private method
final List l = list;
l.set(i, l.set(j, l.get(i)));
}
/**
* 交换指定数组中的两个指定元素
*/
private static void swap(Object[] arr, int i, int j) {
Object tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
/**
* 在第一种情况中,Arrays.asList()的输出被传递给了ArrayList()的构造器,这将创建一个引用ia的元素的ArrayList,因此打乱这些引用不会修改该数组。
*
* 但是,如果直接使用Arrays.asList(ia)的结果, 这种打乱就会修改ia的顺序。
*
* 意识到Arrays.asList()产生的List对象会使用底层数组作为其物理实现是很重要的。
*
* 只要你执行的操作 会修改这个List,并且你不想原来的数组被修改,那么你就应该在另一个容器中创建一个副本。
* @param args
*/
public static void main(String[] args) {
Random rand = new Random(47);
Integer[] ia = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
System.out.println("array: " Arrays.toString(ia));
System.out.println("-------------------------------");
List<Integer> list = new ArrayList<Integer>(Arrays.asList(ia));
System.out.println("Before shufflig: " list);
shuffle(list, rand);
System.out.println("After shuffling: " list);
System.out.println("-------------------------------");
List<Integer> list1 = Arrays.asList(ia);
System.out.println("Before shuffling: " list1);
shuffle(list1, rand);
System.out.println("After shuffling: " list1);
System.out.println("-------------------------------");
System.out.println("array: " Arrays.toString(ia));
}
}
当 List 长度小于 SHUFFLE_THRESHOLD(定义为5)或者是 RandomAccess 的实例时,直接以 List 的数据结构进行打乱,否则转为数组再打乱,最后转储回 List。
这里扩展一下,我们可以用这个方法实现一个洗牌功能,具体代码如下:
代码语言:javascript复制/**
* 洗牌
* @author lixiaojin
* @date 2021/11/17 21:20
*/
public class Shuffle {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
// 将52个数字放入一个List中
for(int i=0; i<52; i ){
list.add(i);
}
// 随机打乱list数据
Collections.shuffle(list);
//按要求输出
String[] flowers = {"黑桃", "红心", "梅花", "方块"};
String[] numbers = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
for(int i=0; i<52; i ){
System.out.print(flowers[list.get(i)%4] numbers[list.get(i)/4] " ");
if(i == 12){
System.out.print('n');
}
}
}
}
Copyright: 采用 知识共享署名4.0 国际许可协议进行许可 Links: https://lixj.fun/archives/关于collections-shuffle方法源码阅读