在JAVA的JDK中Collections類提供了shuffle方法用來對給定的集合參數進行亂序重排,以前面試也被問到過相似的問題,看了一下JDK的源碼實現作個記錄面試
1. 方法簽名:數組
Collections.shuffle方法提供了兩個重載的形式分別爲:數據結構
1. public static void shuffle(List<?> list) 2. public static void shuffle(List<?> list, Random rnd)
在實現上,第一個方法中new了Random對象,而後調用第二個方法,因此咱們來看第二重載形式的實現。所有代碼以下:dom
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]); } } }
代碼解釋:性能
SHUFFLE_THRESHOLD 爲Collections類中的靜態變量,類型爲整形,默認爲5
if判斷中,首先判斷要亂序的集合大小,若是集合大小<5,或者集合類型實現了RandomAccess接口,則直接調用集合交換方法。ui
RandomAccess是一個空接口,我的的理解和Serializable接口同樣,起到一個標識的做用,在這裏標識集合類是否支持隨機訪問。spa
若是支持則隨機訪問,或者元素個數<5,則直接調用集合交換的swap方法來交換元素(畢竟即便集合不支持RandomAccess,5個code
以內的元素交換也不會影響什麼性能)。對象
再看一下集合元素交換的方法:blog
public static void swap(List<?> list, int i, int j) { final List l = list; l.set(i, l.set(j, l.get(i))); }
就這麼兩行代碼,不過這裏有一點沒看懂的是:爲何要聲明一個final類型的List來接收參數中的List對象?不明白
交換的規則也很簡單,變量 i 是循環內獲取的集合的size值-1,也就是集合的最後一個元素,將最後一個元素的值
設置爲集合中位置 j 的值,j 的值是random.nextInt(i)來隨機獲取的集合中的某個位置索引。
因此交換規則就是:
循環,每次將數組的最後一個元素和一個隨機獲取到的元素進行交換。
再來看else分支中:
能進到else分支,說明集合對象沒有實現RandomAccess接口,好比LinkedList沒有實現RandomAccess,由於
數據結構的特性,若是訪問LinkedList中的元素只能遍歷,若是元素多,訪問的元素還靠後,訪問性能不好,因此JDK在這裏
將集合首先轉爲數組,而後調用數組的元素交換方法,交換規則和以前的規則同樣。由於數組有下標,支持隨機訪問,
因此這樣亂序會提升性能。
private static void swap(Object[] arr, int i, int j) { Object tmp = arr[i]; arr[i] = arr[j]; arr[j] = tmp; }
數組元素交換方法如上。