【问题标题】:Why autoboxing is not supporting in collection.toArray() in javajava - 为什么在java中的collection.toArray()中不支持自动装箱
【发布时间】:2014-06-26 11:01:27
【问题描述】:

有没有捷径可以很好地做到这一点?

那丑陋的两个循环(一个循环读取 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[])

【问题讨论】:

    标签: java arrays collections


    【解决方案1】:

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

    自动装箱仅适用于原始类型,不适用于原始类型数组double[] 数组不是 T[] 数组,因为类型参数 T 必须始终是 Object。虽然double 可以自动装箱为T(使用T=Double),但double[] 不能自动装箱为T[]

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

    如果您必须返回一个 double[] 数组,那么您必须编写自己的函数或使用像 Guava 这样的第三方库(请参阅 msandiford 的回答)。 Java Collections 框架没有对数组进行(取消)装箱的方法。

    【讨论】:

    • @Jayaprasad:是的,请参阅我编辑的答案。装箱根本不适用于数组。
    • @ gexicide :我不能因为依赖关系。那个 markupArray 应该是 double[].
    • @Namalak:你提前知道列表的大小吗?如果是这样,您可以跳过列表并直接插入到数组中。
    • @gexicide:没有。 (不幸)
    • @Namalak:好吧,那就写你自己的方法吧。这四行不会杀死你:)。另一种解决方案是在您的输入上循环两次,第一次只计算双打的数量。这可行吗?
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      为什么不制作自己的快捷方式?

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

      【讨论】:

      • 那只是代码的位移。你是说如果我们使用的是必须在内部执行这两个循环的捷径吗?
      • 任何其他快捷方式都将在内部执行此操作,是的。
      【解决方案4】:

      Google guava 有Doubles#asList(...)Doubles#toArray(...),它们分别提供从double[]List&lt;Double&gt; 和从Collection&lt;? extends Number&gt;double[] 的转换。

      【讨论】:

        【解决方案5】:

        您说得对,乍一看这不是很直观。但是,此限制与 Java 语言实现泛型类型和自动装箱的方式有关:

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

        2. 其次,自动装箱和拆箱仅在编译时存在。这意味着语句

          Integer i = 42;
          int j = i;
          

          就像你写的一样编译

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

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

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

        【讨论】:

          猜你喜欢
          • 2015-02-23
          • 1970-01-01
          • 1970-01-01
          • 2011-01-19
          • 1970-01-01
          • 2016-12-28
          • 2014-01-21
          • 1970-01-01
          相关资源
          最近更新 更多