【问题标题】:Why does Collections.shuffle() fail for my array?为什么 Collections.shuffle() 对我的数组失败?
【发布时间】:2011-04-28 05:47:30
【问题描述】:

为什么我的代码不起作用?

package generatingInitialPopulation;

import java.util.Arrays;
import java.util.Collections;

public class TestShuffle {
    public static void main(String[] args) {
        int[] arr = new int[10];

        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }

        Collections.shuffle(Arrays.asList(arr));

        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
    }
}

结果是:0 1 2 3 4 5 6 7 8 9。

我期待的是一个随机打乱的序列

【问题讨论】:

    标签: java shuffle random-access


    【解决方案1】:

    尝试将这行代码添加到您的测试中:

    List l=Arrays.asList(arr);
    System.out.println(l);
    

    您将看到您正在打印出单个元素 List

    在原始数组上使用Arrays.asList 会导致asListint[] 视为单个对象而不是数组。它返回 List&lt;int[]&gt; 而不是 List&lt;Integer&gt;。因此,您基本上是在对单个元素 List 进行洗牌,因此没有任何东西真正被洗牌。

    请注意,已经给出的一些答案是错误的,因为asList 返回一个由原始数组支持的 List,没有任何内容被复制 - 所有更改都反映在原始数组中。

    【讨论】:

    • 但是为什么这会在原始类型 int(将 int[] 视为单个对象)中以这种方式工作?
    • @Dmitry: 数组在 Java 中也是Objects,我猜asList(T...) 的参数被解释为简单的单个Object,因为asList 不期望原语。跨度>
    【解决方案2】:

    Arrays.asList() 不能像您期望的那样应用于原始类型的数组。当应用于int[] 时,Arrays.asList() 会生成int[]s 的列表,而不是Integers 的列表。因此,您将一个新创建的int[] 列表打乱。

    这是 Java 中可变参数和泛型的一种微妙行为。 Arrays.asList() 被声明为

    public static <T> List<T> asList(T... a)
    

    因此,它可以接受多个T 类型的参数并生成一个包含这些参数的列表,或者它可以接受一个T[] 类型的参数并返回一个由该数组支持的列表(这就是可变参数的工作方式) .

    但是,后一种选项仅在T 是引用类型(即不是基本类型,例如int)时才有效,因为只有引用类型可以用作泛型中的类型参数(而T 是类型参数)。

    因此,如果您通过int[],您将得到T = int[],并且您的代码无法按预期工作。但是如果你传递引用类型的数组(例如,Integer[]),你会得到T = Integer 并且一切正常:

    Integer[] arr = new Integer[10]; 
    
    for (int i = 0; i < arr.length; i++) { 
        arr[i] = i; 
    } 
    
    Collections.shuffle(Arrays.asList(arr)); 
    
    for (int i = 0; i < arr.length; i++) { 
        System.out.print(arr[i] + " "); 
    } 
    

    【讨论】:

    • 哇!!!但是大家都说申请的是列表的变化,不是用数组arr。为什么会这样?
    • 因为 Arrays.asList() 创建了一个由数组支持的新列表。它不会像其他人所说的那样复制数组。 Arrays.asList() 返回的列表的每次更改也会更改支持它的数组。
    • 现在明白了。但为什么这不适用于原始类型?
    • @Dmitry:因为 Java 泛型不适用于原始类型。看,Arrays.asList() 返回 List&lt;T&gt;,但你不能在 Java 中声明 List&lt;int&gt;
    • T 不能是原始类型,因此从某种意义上说,它不应该为原始类型编译。但是,由于 varargs,使用单个对象调用 asList 是有效的,它会将其视为大小为 1 的数组。因此,它将以一种令人困惑的方式编译和工作。
    【解决方案3】:

    存储由 Arrays.asList 返回的列表并随机播放...

    List myShuffledList = Arrays.asList(arr);
    Collections.shuffle(myShuffledList);
    

    【讨论】:

    • 使用泛型类型而不是原始类型要好得多
    【解决方案4】:

    这不起作用,因为对shuffle 的调用是在Arrays.asList 返回的List 上运行,而不是在底层数组上运行。因此,当您遍历数组以打印出值时,没有任何变化。您要做的是保存对Arrays.asList 返回的List 的引用,然后在shuffle 之后打印出List 的值(而不是数组的值)。

    【讨论】:

    • API 说 - “返回由指定数组支持的固定大小的列表。(对返回的列表的更改“写入”到数组。)” - download.oracle.com/javase/6/docs/api/java/util/…
    • 感谢您的即时答复!
    • 错误,对Arrays.asList() 创建的列表所做的任何更改都是对数组本身进行的——列表使用该数组来存储元素。问题是asList 无法创建原语列表,因此它创建了一个包含一个元素的列表:数组本身。它适用于具有非基元(例如整数)的数组。
    猜你喜欢
    • 2023-04-07
    • 2017-09-03
    • 1970-01-01
    • 2015-03-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-16
    相关资源
    最近更新 更多