Collections.shuffle()源码分析

Collections.shuffle()源码分析Java.util.Collections类下有一个静态的shuffle()方法,如下:1)staticvoidshuffle(List<?>list)使用默认随机源对列表进行置

大家好,又见面了,我是你们的朋友全栈君。

Java.util.Collections类下有一个静态的shuffle()方法,如下:

  1)static void shuffle(List<?> list)  使用默认随机源对列表进行置换,所有置换发生的可能性都是大致相等的。

  2)static void shuffle(List<?> list, Random rand) 使用指定的随机源对指定列表进行置换,所有置换发生的可能性都是大致相等的,假定随机源是公平的。

源码展示:

public class Collections {
    private static Random r;
    private static final int SHUFFLE_THRESHOLD = 5;
    
    public static void shuffle(List<?> list) {
        if (r == null) {
            r = new Random();
        }
        shuffle(list, r);
    }
    
    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
            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) {
        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;
    }
    
}

基本应用(打乱一个int数组):

public class ShuffleTest {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<Integer>();
        for (int i = 0; i < 10; i++)
            list.add(new Integer(i));
        System.out.println("打乱前:");
        System.out.println(list);
 
        for (int i = 0; i < 5; i++) {
            System.out.println("第" + i + "次打乱:");
            Collections.shuffle(list);
            System.out.println(list);
        }
    }
}

经典应用(洗牌):

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class CollectionsShuffle {
    public static void main(String[] args) {
        List<Card> cards = new ArrayList<Card>();
        // 生成一副牌
        for (int rank = Card.THREE; rank <= Card.DEUCE; rank++) {
            cards.add(new Card(Card.DIAMOND, rank));
            cards.add(new Card(Card.CLUB, rank));
            cards.add(new Card(Card.HEART, rank));
            cards.add(new Card(Card.SPADE, rank));
        }
        cards.add(new Card(Card.JOKER, Card.BLACK));
        cards.add(new Card(Card.JOKER, Card.COLOR));
        System.out.println(cards.toString());
        /*
         * [方块3, 梅花3, 红桃3, 黑桃3, 方块4, 梅花4, 红桃4, 黑桃4, 方块5, 梅花5, 红桃5, 黑桃5, 方块6,
         * 梅花6, 红桃6, 黑桃6, 方块7, 梅花7, 红桃7, 黑桃7, 方块8, 梅花8, 红桃8, 黑桃8, 方块9, 梅花9, 红桃9,
         * 黑桃9, 方块10, 梅花10, 红桃10, 黑桃10, 方块J, 梅花J, 红桃J, 黑桃J, 方块Q, 梅花Q, 红桃Q, 黑桃Q,
         * 方块K, 梅花K, 红桃K, 黑桃K, 方块A, 梅花A, 红桃A, 黑桃A, 方块2, 梅花2, 红桃2, 黑桃2, 小王, 大王]
         */
        // 经典洗牌算法
        Random random = new Random();
        for (int i = cards.size(); i > 1; i--) {
            int m = random.nextInt(i);
            swap(cards, i - 1, m);
        }
        System.out.println(cards.toString());
        /*
         * [黑桃7, 黑桃A, 梅花A, 红桃9, 梅花4, 红桃K, 方块5, 梅花7, 梅花6, 方块A, 黑桃Q, 梅花5, 红桃10,
         * 梅花Q, 梅花J, 方块J, 梅花K, 方块8, 方块6, 方块10, 红桃7, 方块K, 红桃6, 黑桃2, 黑桃K, 梅花10,
         * 红桃8, 方块Q, 红桃Q, 大王, 梅花3, 梅花2, 方块7, 方块9, 方块4, 红桃3, 梅花9, 红桃J, 黑桃8, 红桃2,
         * 黑桃6, 红桃A, 黑桃9, 黑桃4, 黑桃J, 黑桃10, 小王, 黑桃3, 黑桃5, 红桃5, 红桃4, 方块2, 方块3, 梅花8]
         */

    }

    public static void swap(List<?> list, int i, int j) {
        final List l = list;
        l.set(i, l.set(j, l.get(i)));
    }
}

class Card {

    public static final int DIAMOND = 0; // 方块(钻石)
    public final static int CLUB = 1; // 梅花
    public static final int HEART = 2; // 红桃(红心)
    public static final int SPADE = 3; // 黑桃(花锄)
    public static final int JOKER = 4; //

    public final static int THREE = 0;
    public final static int FOUR = 1;
    public final static int FIVE = 2;
    public final static int SIX = 3;
    public final static int SEVEN = 4;
    public final static int EIGHT = 5;
    public final static int NINE = 6;
    public final static int TEN = 7;
    public final static int JACK = 8;// J
    public final static int QUEEN = 9;// Q
    public final static int KING = 10;// K
    public final static int ACE = 11;// A
    public final static int DEUCE = 12; // 2
    public final static int BLACK = 13; // 小王
    public final static int COLOR = 14;// 大王

    /** 花色 0代表方块, 1代表梅花, 2代表红桃, 3代表黑桃,4:王 */
    private int suit;
    /** 点数 规定: 0代表3, 1代表4, 2代表5,... */
    private int rank;

    public Card() {
    }

    public Card(int suit, int rank) {
        // this.rank = rank;
        // this.suit = suit;
        setRank(rank);
        setSuit(suit);
    }

    public int getSuit() {
        return suit;
    }

    public void setSuit(int suit) {
        if (suit < DIAMOND || suit > JOKER)
            throw new RuntimeException("花色超过范围!");
        this.suit = suit;
    }

    public int getRank() {
        return rank;
    }

    public void setRank(int rank) {
        if (rank < THREE || rank > COLOR) {
            throw new RuntimeException("点数超过范围!");
        }
        this.rank = rank;
    }

    private static final String[] RANK_NAMES = { "3", "4", "5", "6", "7", "8",
            "9", "10", "J", "Q", "K", "A", "2", "小王", "大王" };
    private static final String[] SUIT_NAMES = { "方块", "梅花", "红桃", "黑桃", "" };

    // 覆盖Object 类的toStirng() 方法. 实现对象的文本描述
    public String toString() {
        return SUIT_NAMES[suit] + RANK_NAMES[rank];
    }

    public boolean equals(Object obj) {
        if (obj == null) {
            return false;
        }
        if (this == obj) {
            return true;
        }
        if (obj instanceof Card) {
            Card other = (Card) obj;
            return this.rank == other.rank && this.suit == other.suit;
        }
        return false;
    }

    public int hashCode() {
        // return suit*100+rank;
        // suit=3= 00000000 00000000 00000000 00000011
        // rank=10=00000000 00000000 00000000 00000011
        // suit<<16=00000000 00000011 00000000 00000000
        // 00000000 00000011 00000000 00000011
        return (suit << 16) + rank;// (x<<16)+y
    }
}

注意:如果给定一个整型数组,用Arrays.asList()方法将其转化为一个集合类,有两种途径:

  1)用List<Integer> list=ArrayList(Arrays.asList(ia)),用shuffle()打乱不会改变底层数组的顺序。

  2)用List<Integer> list=Arrays.aslist(ia),然后用shuffle()打乱会改变底层数组的顺序。代码例子如下:

public class Modify {  
    public static void main(String[] args){  
        Random rand=new Random(47);  
        Integer[] ia={0,1,2,3,4,5,6,7,8,9};  
        List<Integer> list=new ArrayList<Integer>(Arrays.asList(ia));  
        System.out.println("Before shufflig: "+list);  
        Collections.shuffle(list,rand);  
        System.out.println("After shuffling: "+list);  
        System.out.println("array: "+Arrays.toString(ia));  
        List<Integer> list1=Arrays.asList(ia);  
        System.out.println("Before shuffling: "+list1);  
        Collections.shuffle(list1,rand);  
        System.out.println("After shuffling: "+list1);  
        System.out.println("array: "+Arrays.toString(ia));  
          
    }  
}  

结果如下:

    Before shufflig: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]  
    After shuffling: [3, 5, 2, 0, 7, 6, 1, 4, 9, 8]  
    array: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]  
    Before shuffling: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]  
    After shuffling: [8, 0, 5, 2, 6, 1, 4, 9, 3, 7]  
    array: [8, 0, 5, 2, 6, 1, 4, 9, 3, 7]  

  在第一种情况中,Arrays.asList()的输出被传递给了ArrayList()的构造器,这将创建一个引用ia的元素的ArrayList,因此打乱这些引用不会修改该数组。 但是,如果直接使用Arrays.asList(ia)的结果, 这种打乱就会修改ia的顺序。意识到Arrays.asList()产生的List对象会使用底层数组作为其物理实现是很重要的。 只要你执行的操作 会修改这个List,并且你不想原来的数组被修改,那么你就应该在另一个容器中创建一个副本。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/155268.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • java 开源 报表_6款常用的Java开源报表制作工具

    java 开源 报表_6款常用的Java开源报表制作工具原标题:6款常用的Java开源报表制作工具1.Aspose.CellsforJasperReports是一个基于Java的开源报表工具,它可以在Java环境下像其他IDE报表工具一样来制作报表,支持PDF、HTML、XLS、CSV和XML文件输出格式,是当前Java开发者最常用的报表工具。<Aspose.CellsforJasperReports试用版下载>2.BIRT…

    2022年10月20日
    2
  • java volatile原理

    java volatile原理一、基本概念先补充一下概念:Java内存模型中的可见性、原子性和有序性。可见性:  可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉。通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。  可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是…

    2022年7月18日
    14
  • webrtc开发入门_统计的简单应用

    webrtc开发入门_统计的简单应用WebRTC介绍及简单应用WebRTC,即WebRealTimeCommunication,web实时通信技术。简单地说就是在web浏览器里面引入实时通信,包括音视频通话等。WebRT

    2022年8月1日
    5
  • oracle命令创建新用户

    oracle命令创建新用户一、sqlplus连接oracle1、sqlplus登录Windows需要sqlplus命令框,获取CMD窗口下输入sqlplus(需要先安装成功oracle)2、输入用户名和口令(密码)3、以sysdba身份连接oracleconnsys/密码assysdba4、查看当前查看当前实例名selectinstance_namefr…

    2022年5月19日
    58
  • 下标「建议收藏」

    下标「建议收藏」下标下标可以定义在类、结构体和枚举中,是访问集合、列表或序列中元素的快捷方式。可以使用下标的索引,设置和获取值,而不需要再调用对应的存取方法。举例来说,用下标访问一个Array实例中的元素可以写

    2022年8月3日
    6
  • IP地址和域名的关系

    IP地址和域名的关系1、ip地址和域名是一对多的关系,一个ip地址可以有多个域名,但是相反,一个域名只能有一个ip地址;2、ip地址是数字型的,为了方便记忆,才有了域名,通过域名地址就能找到ip地址;3、ip,全称为互联网协议地址,是指ip地址,意思是分配给用户上网使用的网络协议的设备的数字标签;4、常用的ip地址分为IPv4和IPv6两大类;什么是IP地址1、IP地址是IP协议提供的一种统一的地址格式,他为互联网上的每一台主机和每一个网络都分配一个唯一的逻辑地址,以此来屏蔽物理地址的差异;

    2022年4月5日
    87

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号