Collections.shuffle()源碼分析
阿新 • • 發佈:2017-06-22
spa tor eight gets ext obj bsp 傳遞 給定
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; privatestatic 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,並且你不想原來的數組被修改,那麽你就應該在另一個容器中創建一個副本。
Collections.shuffle()源碼分析