zoukankan      html  css  js  c++  java
  • Collections.shuffle()源码分析

    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,并且你不想原来的数组被修改,那么你就应该在另一个容器中创建一个副本。

  • 相关阅读:
    ssh框架构建项目详解--基本概念和struts2
    maven 工程启动找不到 Spring ContextLoaderListener 的解决办法
    ssh(struts,spring,hibernate)开发的初步集成02--xml文件配置
    ssh(struts,spring,hibernate)开发的初步集成01--依赖
    Maven--01(概念)
    hibernate入门知识-03-对象关系映射
    [LeetCode]Construct Binary Tree from Preorder and Inorder Traversal
    [LeetCode]Plus One
    [LeetCode]Triangle
    [LeetCode]PASCAL Triangle系列
  • 原文地址:https://www.cnblogs.com/jing99/p/7062171.html
Copyright © 2011-2022 走看看