【问题标题】:How do I convert Double[] to double[]?如何将 Double[] 转换为 double[]?
【发布时间】:2010-11-09 17:55:47
【问题描述】:

我正在实现一个接口,其功能类似于可以包含某种对象类型的表。接口指定如下函数:

double[] getDoubles(int columnIndex);

让我感到困惑的是,在我的实现中,我将表数据存储在二维 Object 数组 (Object[][] data) 中。当我需要返回值时,我想做以下事情(假设getDoubles()只会在包含双精度的列上被调用,所以不会有ClassCastExceptions):

double[] getDoubles(int columnIndex) {
    return (double[]) data[columnIndex];
}

但是 - Java 不允许将 Object[] 强制转换为 double[]。将其转换为 Double[] 是可以的,因为 Double 是一个对象,而不是原语,但我的界面指定数据将作为 double[] 返回。

所以我有两个问题:

  1. 有什么方法可以从Object[][] 表中取出列数据并返回基元数组?
  2. 如果我将接口更改为返回Double[],是否会对性能产生影响?

【问题讨论】:

  • 为什么需要将数据作为 Object[][]?
  • 该表可以存储任何类型的数据:字符串、双精度、整数、其他类型等
  • 那你怎么能保证 columnIndex 的数组只包含双精度数/双精度数呢?
  • 使用该接口的任何人只负责在包含双精度的列上调用 getDoubles()。

标签: java arrays autoboxing


【解决方案1】:

如果您不介意使用 3rd 方库,commons-lang 具有 ArrayUtils 类型,其中包含各种操作方法。

Double[] doubles;
...
double[] d = ArrayUtils.toPrimitive(doubles);

还有互补的方法

doubles = ArrayUtils.toObject(d);

编辑:回答剩下的问题。这样做会有一些开销,但除非数组真的很大,否则你不应该担心它。在重构之前先测试一下是否有问题。

实施您实际询问的方法会得到类似的结果。

double[] getDoubles(int columnIndex) {
    return ArrayUtils.toPrimitive(data[columnIndex]);
}

【讨论】:

  • 感谢 commons-lang 具有该功能的信息,如果我决定使用任何 3rd 方库,它将非常有用
  • 我更喜欢这个而不是循环遍历的答案。对于样板代码使用第三方 API 会更好
  • 使用第三方库可能看起来不错(毕竟,单行代码看起来比丑陋的 for 循环好多了......)但实现与手动 Double-to 没有什么不同-双重转换。最差!甚至每次迭代都有一个三元运算符,这可能会导致更多的计算机处理。我的观点是,“首选”解决方案应该考虑性能和实用性而不是代码美学。 :) 我的 2c。
  • 作为一种折衷方案,您可以将丑陋的代码隐藏在静态 Utils 类中,并进行几个单元测试,以确保您可以依赖它。这样您的代码看起来很棒,而且您无需担心性能或依赖第三方库。
【解决方案2】:

在 Java 8 中,这是单行代码:

Double[] boxed = new Double[] { 1.0, 2.0, 3.0 };
double[] unboxed = Stream.of(boxed).mapToDouble(Double::doubleValue).toArray();

请注意,这仍然会遍历原始数组并创建一个新数组。

【讨论】:

  • 这就是我要找的东西!
【解决方案3】:

不幸的是,如果要将其转换为 double[],则需要遍历整个列表并将 Double 拆箱。

就性能而言,Java 中的装箱和拆箱原语需要一段时间。如果集合足够小,您不会看到任何性能问题。

【讨论】:

  • 虽然不直接适用,但在 C# 中对具有原始类型和盒装类型的大型(一百万个元素)数组进行排序的测试表明,原始排序的速度是原始排序的 3 倍。我提到这一点是因为它说明自动装箱/拆箱成本可能很高。
  • 哇。这比我预期的要重要得多。不过,我不会认为 100 万是一小部分。
【解决方案4】:

您可以使用 for each 循环来构造一个大小相同的临时数组,然后将每个单独的元素转换为双精度,但它在数组中。

所以:

double[] tempArray = new double[data[columnIndex].length];
int i = 0;
for(Double d : (Double) data[columnIndex]) {
  tempArray[i] = (double) d;
  i++;
}

如果我在这里错了,请纠正我。

【讨论】:

  • 这个其实比其他的更简单
  • @Ryde 如果简单地说,您的意思是缺少 FOP 结构和第三方依赖项,那么可以。但是,如果您有这些东西中的任何一个可供您使用,那么我认为这比必要的代码要多。
【解决方案5】:

如果您想返回 double[],则需要创建 new double[],填充它,然后返回。

这可能是一个很好的架构决策。首先,将Object[] 转换为Double[] 没有多大意义;它并不是真正的Double 数组,因为其中也可能有Objects。其次,如果直接返回数组,用户代码可以修改它并改变对象的内部结构。

由于拆箱和分配成本,主要的性能影响是返回 double[] 数组。

【讨论】:

    【解决方案6】:

    您可以利用 ArrayUtils 进行转换

    Double[] d; // initialise
    double[] array = ArrayUtils.toPrimitive(d);
    

    无需循环整个数据。

    【讨论】:

      【解决方案7】:

      对于真正的问题,除了 jjnguy 和 Eric Koslow 所说的之外,我没有什么要补充的。

      但只是附带说明:您提到将 Object 数组转换为 Double 数组。以下将不起作用:

      Object[] oa=new Object[3];
      oa[0]=new Double("1.0");
      oa[1]=new Double("2.0");
      oa[2]=new Double("3.0");
      Double[] da=(Double[])oa;
      

      最后一行将抛出一个类转换异常。尽管数组中的每个元素确实是一个 Double,但该数组是作为 Objects 数组创建的,而不是 Doubles 数组,因此强制转换无效。

      【讨论】:

        【解决方案8】:

        我会支持 ArrayUtils 的答案,并补充说 1.5 autoboxing documentation(via) 有点表明没有内置方法:

        如果不允许,则不允许从数组类型 SC[] 转换为数组类型 TC[] 从 SC 到 TC 的字符串转换以外的转换

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-01
          • 2016-10-10
          • 1970-01-01
          • 2019-07-08
          • 1970-01-01
          相关资源
          最近更新 更多