在Java中扩展数组的最有效方法是什么?

14

(如果此前已经有人提出过这个问题,我很抱歉——我不敢相信没有人问过,但我找不到。也许是我的搜索能力太弱了。)

多年来,我一直“知道”Java没有本地函数可以缩放数组(即将每个元素乘以一个常数)。因此,我一直在做这件事:

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

这实际上是最有效的方式吗(例如在这个应用程序中,它是大约10000个双精度数组)?还是有更好的方法?


4
如果你对微小优化感兴趣,那么你所写的内容通常可以通过使用“循环展开”来加速:http://en.wikipedia.org/wiki/Loop_unwinding。然而,如果该代码部分被重复调用,JVM可能已经为你执行了这个操作。HotSpot VM甚至有一个*XX:LoopUnrollLimit=*选项来“控制”这种行为。 - TacticalCoder
7个回答

14

我认为这个看起来非常好,我想不出更有效的方法了。显然,尽量将该代码放在一个地方而不是到处都是实际代码,除此之外,没有明显的问题。


8

我唯一能提供的建议是懒惰缩放,这意味着您只需要在访问每个元素时支付乘法成本;例如:

public class MyArray {
  private final double[] arr;
  private double scale = 1.0;

  public MyArray(double[] arr) {
    this.arr = arr;
  }

  public double getScale() {
    return scale;
  }

  public void setScale(double scale) {
    this.scale = scale;
  }

  public double elementAt(int i) {
    return arr[i] * scale;
  }
}

显然,这种方法只适用于特定情况:

  • 当你的数组非常巨大时,并且
  • 你只访问少数元素,并且
  • 通常只访问这些元素一次。

在其他情况下,这是一种微小的优化,在现代CPU上没有真正的好处。


在这种情况下,最好内部使用ArrayList<T>,并在请求时将其压缩成数组。 - 0xCAFEBABE
@0xCAFEBABE:我认为这完全取决于原帖作者是否期望数组大小会变化。如果不会,最好节省内存并使用基本数据类型。 - Adamski
我的应用程序将一次性读取整个固定大小的数组,因此我猜“懒惰”缩放和使用ArrayList都不太高效。 - Ian Renton

5
“更好的方法”是写成array[i] *= scaleFactor;而不是array[i] = array[i] * scaleFactor;。:-)
实际上,这只是语法糖 - 编译输出(因此性能)应该完全相同。正如Jon所说,您无法获得任何更好的性能,但个人认为,我每天都会减少输入量。

1
尽管我喜欢使用*=的写法,但该回答完全超出了问题的范围,可能更适合作为评论。 - Sanjay Verma

3

除了Adamski和Jon Skeet提到的内容,我想补充一点:如果它恰好是一个整数/长整数数组,并且您要缩放2的幂,则使用位移运算符可能会稍微提高性能。但是,这取决于编译器(甚至可能是虚拟机),因此可能会有所不同。


这将取决于虚拟机。肯定是依赖于编译器而不是虚拟机吧? - Raedwald
嗯,是的。已经更正了。这也可能取决于硬件(位移可能需要像乘法一样长的时间),所以我还是坚持我的观点。 :) - vaughandroid
@Raedwald:这将取决于JIT编译器,它通常被认为是VM的一部分。优化不被视为Java到字节码编译器的工作。 - Michael Borgwardt

2
在Java 8中:
double coef = 3.0;
double[] x1 = {1,2,3};
double[] x2 = DoubleStream.of(x1).map(d->d*coef).toArray();

System.out.println(Arrays.toString(x2));

output: [3.0, 6.0, 9.0]


0
你可以使用线程来减少运行时间,但归根结底,你需要将这段代码包含进去,并让每个线程运行 for 循环的一部分,以便得到与你的程序同样高效的结果;只不过速度更快。

4
代码(我非常确定)是受内存限制而不是 CPU 限制的,因此更多的线程/CPU 动力不会有任何区别。我猜它只会运行得更慢。但是,如果你编写一个程序来证明我错了,我会点赞! - Ishtar

0

在我看来,这看起来是最优的。

不要被虚假的优化所迷惑,例如在循环外部的final字段中声明数组长度。这对于集合可以避免重复调用.size()方法和字符串避免调用.length()方法,但是对于数组而言,.length已经是一个公共的final字段。

此外,向零反向循环可能是汇编语言优化,但在像Java这样的高级语言中,虚拟机会处理任何明显的微调。


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