关于Collections.shuffle()方法源码阅读

2022-06-10 18:39:20 浏览数 (3)

这个方法是在看原型模式的示例代码时用到的,觉得挺有意思的,研究一下~

通过查看 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方法源码阅读

0 人点赞