如何将Double[]转换为double[]?

74

我正在实现一个类似于可以包含各种对象类型的表格的接口。该接口指定了以下功能:

double[] getDoubles(int columnIndex);

我困惑的地方在于,我的实现中将表格数据存储在一个2D Object数组(Object[][] data)中。当需要返回这些值时,我希望按照以下方式进行操作(假设getDoubles()仅会在包含双精度值的列上调用,因此不会出现ClassCastExceptions):

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

但是Java不允许将Object[]强制转换为double[]。将其转换为Double[]是可以的,因为Double是一个对象而不是原始类型,但我的接口规定数据将作为double[]返回。

所以我有两个问题:

  1. 是否有任何方法可以从Object[][]表中获取列数据并返回原始数组?
  2. 如果我更改接口以返回Double[],是否会对性能产生影响?

为什么需要将数据作为Object[][]? - notnoop
该表可以存储任何类型的数据:字符串、双精度浮点数、整数、其他类型等。 - Brian
那么你如何确保列索引处的数组只包含 Double/double 类型呢? - notnoop
任何使用接口的人都应该只在包含双精度浮点数的列上调用getDoubles()。 - Brian
8个回答

96

如果你不介意使用第三方库,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具有该功能的信息,如果我决定使用任何第三方库,这将在未来非常有用。 - Brian
我更喜欢使用第三方API来处理样板代码,而不是通过循环的方式来解决问题。这样做更好。 - Richard
5
使用第三方库可能看起来很不错(毕竟,一行代码比丑陋的for循环好多了……),但实现与手动双精度转换没有区别。更糟糕的是,每次迭代甚至会有一个三元运算符,这可能会导致更多的计算机处理。我的观点是,“偏爱”解决方案应该考虑性能和实用性而不是代码美学。 :) 我的个人意见。 - Yanick Rochon
作为一种妥协,你可以将丑陋的代码隐藏在一个静态的 Utils 类中,并进行一些单元测试,以确保你可以依赖它。这样你的代码看起来很棒,而且你不需要担心性能或依赖第三方库。 - Andras Balázs Lajtha

58

在Java 8中,这可以用一行代码实现:

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

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


1
这就是我一直在寻找的! - Hamza Abbad

34

很不幸,如果您想将其转换为double[],则需要遍历整个列表并取消装箱Double

就性能而言,在Java中装箱和取消装箱基元类型是需要一定时间的。如果集合足够小,则不会出现性能问题。


4
尽管不是直接相关的,但在对包装和基本类型进行排序的C#测试中,对于大型数组(一百万元素),基本类型的排序速度要快3倍。我提到这一点是因为它说明自动装箱/拆箱的成本可能非常显著。 - cletus
哇。这比我预料的要重要得多。但我不认为100万是一个小集合。 - jjnguy

8
你可以使用 for each 循环构造一个临时数组,其大小与原数组相同,然后将每个单独的元素强制转换为 double 类型并放入数组中。
因此,代码如下:
double[] tempArray = new double[data[columnIndex].length];
int i = 0;
for(Double d : (Double) data[columnIndex]) {
  tempArray[i] = (double) d;
  i++;
}

请纠正我是否完全错误。

这个解决方案实际上比其他的更简单。 - Ryde
如果你所说的“更简单”是指缺少FOP结构和第三方依赖,那么是的。但是,如果你有这些东西可用,那么我会认为这比必要的代码多得多。 - Lezorte

3
如果您想返回一个 double[],您需要创建一个 new double[],填充它,并返回该数组。这可能是一个很好的架构决策。首先,将 Object[] 强制转换为 Double[] 没有太多意义;它不是真正的 Double 数组,因为其中可能还有 Object。其次,如果直接返回数组,则用户代码可以修改它并更改对象的内部结构。主要的性能影响在于返回一个 double[] 数组,由于拆箱和分配成本。

3
你可以使用ArrayUtils来进行转换。
Double[] d; // initialise
double[] array = ArrayUtils.toPrimitive(d);

无需循环整个数据。

1

除了jjnguy和Eric Koslow所说的内容外,我没有任何可以补充到真正问题上的东西。

但是只是一个侧面的注意事项:您提到将对象数组强制转换为双精度数组。以下操作不起作用:

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类型,但该数组是作为对象数组而不是Double数组创建的,因此强制转换无效。


0
我赞成ArrayUtils的答案,并补充说,1.5 自动装箱文档通过)有点暴露出没有内置的方式:

如果除了从SC到TC的字符串转换之外,没有其他允许的转换,则不允许从数组类型SC[]转换为数组类型TC[]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接