为什么autoboxing不支持java中的collection.toArray()

时间:2014-06-26 11:01:27

标签: java arrays collections

没有任何捷径可以做得很好吗?

那个丑陋的两个循环(一个循环读取pmList,第二个循环添加到markupArray)是唯一的选择(而不是ArrayUtils)。

ArrayList<Double> pmList = new ArrayList<Double>();     
pmList.add(0.1); // adding through a loop in real time.
pmList.add(0.1);
pmList.add(0.1);
pmList.add(0.1);

double[] markupArray = new double[pmList.size()];
arkupArray = pmList.toArray(markupArray); // This says The method toArray(T[]) in the type ArrayList<Double> is not applicable for the arguments (double[])

5 个答案:

答案 0 :(得分:3)

只需使用Double[]数组,而不是double[],那么一切正常。如果您提前知道列表的大小,也可以跳过列表并直接插入到数组中。甚至可能值得两次遍历输入:一次用于检索其大小,一次用于插入。

自动装箱仅适用于基本类型,不适用于基本类型数组double[]数组不是T[]数组,因为类型参数T必须始终为Object。虽然double可以自动装箱到TT=Double),但double[]无法自动装箱到T[]

数组未自动装箱的原因可能是此操作非常昂贵:装箱阵列意味着创建一个新阵列并装箱每个元素。对于大型阵列,这会带来巨大的性能影响。您不希望隐式执行如此昂贵的操作,因此不会对数组进行自动装箱。另外,装入一个完整的数组会产生一个新的数组。因此,在写入新数组时,更改将不会写入旧数组。所以你看,数组装有一些语义问题,所以不支持它。

如果必须返回double[]数组,则必须编写自己的函数或使用像Guava这样的第三方库(请参阅msandiford的答案)。 Java Collections框架没有(un)装箱数组的方法。

答案 1 :(得分:1)

您可以使用TDoubleArraList或guava的原始列表集合。

您还可以在一个循环中预先确定大小,并将值添加到另一个循环中。

答案 2 :(得分:1)

为什么不制作自己的捷径?

static double[] doubleListToArray(List<Double> list) {
    int k = 0;
    double[] result = new double[list.size()];
    for(double value : list)
        result[k++] = value;
    return result;
}

答案 3 :(得分:1)

Google guava有Doubles#asList(...)Doubles#toArray(...),分别提供从double[]List<Double>和从Collection<? extends Number>double[]的转化。

答案 4 :(得分:1)

你是对的,初看起来不是很直观。但是,此限制与Java语言实现泛型类型和自动装箱的方式有关:

  1. 通用类型在运行时被删除。这意味着任何ArrayList<Double>都由单个已编译的Java类ArrayList表示,该类与ArrayList的其他通用表示形式共享,例如ArrayList<String>。因此,编译后的方法ArrayList::toArray不会(也绝不)知道实例表示的泛型类型,因为单个编译方法必须适用于任何泛型类型。因为元素可以是StringDouble,所以您需要为方法提供一个数组。然后,该方法可以在运行时检查目标数组的类型,并检查在运行时填充到数组中的元素是否可分配给数组的组件类型。所有这些逻辑都可以通过单个编译方法实现。

  2. 其次,自动装箱和-unboxing只在编译时存在。这意味着陈述

    Integer i = 42;
    int j = i;
    
    像编写

    一样编译

    Integer i = new Integer(42);
    int j = i.intValue();
    

    Java编译器会为您添加装箱说明。 Java运行时应用稍微不同的类型系统,其中不考虑装箱。因此,我们在(1)中提到的单个编译方法ArrayList::toArray无法知道需要应用此装箱,因为我们认为该方法必须适用于任何类型T,这可能并非总是如此代表Double

  3. 理论上,您可以更改ArrayList::toArray的实现,以显式检查数组的组件类型和列表元素类型是否适用于拆箱,但这种方法会导致几个分支会增加相当方法的运行时开销。相反,编写一个专门用于Double类型的小实用程序方法,并应用由于特化而隐式拆箱。对所有列表项的迭代就足够了,这也是ArrayList::toArray的实现方式。如果您的数组很小,请考虑使用一组加框值Double[]而不是double[]。如果您的阵列很大,使用寿命很长,或者为了符合第三方API而限制为原始类型,请使用该实用程序。另外,如果你想要进行整体拳击,请注意原始集合的实现。使用Java 8,使用Stream来内联阵列转换。