灰气球

灰气球

Java 集合打散Collections.shuffle的用法

191
2023-10-08

shuffle(List<?> list, Random rnd)

/**
 * Randomly permute the specified list using the specified source of
 * randomness.  All permutations occur with equal likelihood
 * assuming that the source of randomness is fair.<p>
 *
 * This implementation traverses the list backwards, from the last element
 * up to the second, repeatedly swapping a randomly selected element into
 * the "current position".  Elements are randomly selected from the
 * portion of the list that runs from the first element to the current
 * position, inclusive.<p>
 *
 * This method runs in linear time.  If the specified list does not
 * implement the {@link RandomAccess} interface and is large, this
 * implementation dumps the specified list into an array before shuffling
 * it, and dumps the shuffled array back into the list.  This avoids the
 * quadratic behavior that would result from shuffling a "sequential
 * access" list in place.
 *
 * @param  list the list to be shuffled.
 * @param  rnd the source of randomness to use to shuffle the list.
 * @throws UnsupportedOperationException if the specified list or its
 *         list-iterator does not support the <tt>set</tt> operation.
 */
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]);
        }
    }
}

使用指定的随机源随机排列指定的列表。假设随机性来源是公平的,所有排列都以相同的可能性发生。
此实现向后遍历列表,从最后一个元素到第二个元素,反复将随机选择的元素交换到“当前位置”。元素是从列表中从第一个元素到当前位置(含)的部分中随机选择的。
该方法以线性时间运行。如果指定的列表未实现 {@link RandomAccess} 接口并且很大,则此实现会在打乱指定列表之前将其转储到数组中,并将打乱后的数组转储回列表中。这避免了因对“顺序访问”列表进行改组而导致的二次行为。
@param list 要洗牌的列表。
@param rnd 用于洗牌列表的随机性来源。
如果指定的列表或其列表迭代器不支持 set 操作,则抛出 UnsupportedOperationException。

shuffle(List<?> list)

/**
 * Randomly permutes the specified list using a default source of
 * randomness.  All permutations occur with approximately equal
 * likelihood.
 *
 * <p>The hedge "approximately" is used in the foregoing description because
 * default source of randomness is only approximately an unbiased source
 * of independently chosen bits. If it were a perfect source of randomly
 * chosen bits, then the algorithm would choose permutations with perfect
 * uniformity.
 *
 * <p>This implementation traverses the list backwards, from the last
 * element up to the second, repeatedly swapping a randomly selected element
 * into the "current position".  Elements are randomly selected from the
 * portion of the list that runs from the first element to the current
 * position, inclusive.
 *
 * <p>This method runs in linear time.  If the specified list does not
 * implement the {@link RandomAccess} interface and is large, this
 * implementation dumps the specified list into an array before shuffling
 * it, and dumps the shuffled array back into the list.  This avoids the
 * quadratic behavior that would result from shuffling a "sequential
 * access" list in place.
 *
 * @param  list the list to be shuffled.
 * @throws UnsupportedOperationException if the specified list or
 *         its list-iterator does not support the <tt>set</tt> operation.
 */
public static void shuffle(List<?> list) {
    Random rnd = r;
    if (rnd == null)
        r = rnd = new Random(); // harmless race.
    shuffle(list, rnd);
}

使用默认的随机源随机排列指定的列表。所有排列发生的可能性大致相等。
在前面的描述中使用对冲“大约”是因为默认的随机性源仅大约是独立选择的位的无偏源。如果它是随机选择位的完美来源,那么算法将选择具有完美均匀性的排列。
此实现向后遍历列表,从最后一个元素到第二个元素,反复将随机选择的元素交换到“当前位置”。元素是从列表中从第一个元素到当前位置(含)的部分中随机选择的。
该方法以线性时间运行。如果指定的列表未实现 {@link RandomAccess} 接口并且很大,则此实现会在打乱指定列表之前将其转储到数组中,并将打乱后的数组转储回列表中。这避免了因对“顺序访问”列表进行改组而导致的二次行为。
@param list 要洗牌的列表。
如果指定的列表或其列表迭代器不支持 set 操作,则抛出 UnsupportedOperationException。

使用示例

  • 示例代码
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]