Java可以优化"变异"循环中的BigInteger操作?

时间:2012-05-18 12:28:42

标签: java

我需要处理大于长(> 10 ^ 200)的大数字,所以我使用BigIntegers。我执行的最常见操作是将它们添加到累加器,例如:

BigInteger A = new BigInteger("0");
for(BigInteger n : nums) {
    A = A.add(n);
}

当然,为破坏性操作制作副本是非常浪费的(好吧,只要有足够大的缓冲区可用),所以我想知道Java是否可以以某种方式优化它(我听说有一个MutableBigInteger类没有通过数学公开.java)或者我是否应该编写自己的BigInteger类。

4 个答案:

答案 0 :(得分:2)

是的,java.math.MutableBigInteger用于计算密集型操作的BigInteger类。不幸的是,它被声明为包私有,因此您无法使用它。 Apache Commons库中还有一个“MutableBigInteger”类,但它只是BigInteger的一个可变包装器,对你没有帮助。

  

我想知道Java能否以某种方式优化它......

不......没有上述情况。

  

或者我是否应该编写自己的BigInteger类。

这是一种方法。

另一种方法是下载OpenJDK源代码,找到java.math.MutableBigInteger的源代码,更改其包名和访问权限,并将其合并到代码库中。唯一的障碍是OpenJDK是根据GPL(我认为是GPL-2)获得许可的,如果您使用修改后的类分发代码,则会产生影响。

另见:

答案 1 :(得分:2)

更快的解决方案是绕过Java包的可见性。您可以通过在您自己的项目中创建一个名为java.math的包并创建一个公开类来公开包私有MutableBigInteger,如下所示:

package java.math;

public class PublicMutableBigInteger extends MutableBigInteger {

}

然后你可以导入java.math.PublicMutableBigInteger;并将其用作任何其他类。此解决方案很快,并且不会对您施加任何特定许可。

答案 2 :(得分:2)

编译器可以做的很多,因为它无法知道add方法的作用。这是循环体的生成代码。如您所见,它只是调用add并存储结果。

   25:  iload   5
   27:  iload   4
   29:  if_icmpge       51
   32:  aload_3
   33:  iload   5
   35:  aaload
   36:  astore  6
   38:  aload_1
   39:  aload   6
   41:  invokevirtual   #5; //Method java/math/BigInteger.add:(Ljava/math/BigInteger;)Ljava/math/BigInteger;
   44:  astore_1
   45:  iinc    5, 1
   48:  goto    25

理论上,Java虚拟机运行时系统可能更聪明。例如,它可以检测到一个对象连续覆盖刚分配的另一个对象,并为它们交换两个分配缓冲区。但是,正如我们通过运行以下启用了垃圾收集日志记录的程序所看到的那样,遗憾的是情况并非如此

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Random;

class Test {
    public static void main(String[] args) {
    ArrayList <BigInteger> nums = new ArrayList<BigInteger>();
    final int NBITS = 100;
    final int NVALS = 1000000;

    System.out.println("Filling ArrayList");
    Random r = new Random();
    for (int i = 0; i < NVALS; i++)
        nums.add(new BigInteger(NBITS, r));

    System.out.println("Adding ArrayList values");
    BigInteger A = new BigInteger("0");
    for(BigInteger n : nums) {
        A = A.add(n);
    }

    System.gc();
    }
}

在添加过程中查看垃圾收集调用。

C:\tmp>java -verbose:gc Test
Filling ArrayList
[GC 16256K->10471K(62336K), 0.0257655 secs]
[GC 26727K->21107K(78592K), 0.0304749 secs]
[GC 53619K->42090K(78592K), 0.0567912 secs]
[Full GC 42090K->42090K(122304K), 0.1019642 secs]
[GC 74602K->65857K(141760K), 0.0601406 secs]
[Full GC 65857K->65853K(182144K), 0.1485418 secs]
Adding ArrayList values
[GC 117821K->77213K(195200K), 0.0381312 secs]
[GC 112746K->77245K(228288K), 0.0111372 secs]
[Full GC 77245K->137K(228288K), 0.0327287 secs]

C:\tmp>java -version
java version "1.6.0_25"
Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)

答案 3 :(得分:0)

Java不会对此案例进行任何特殊优化。 BigInteger通常被视为普通类,与其他任何类似(例如,在串联多个字符串时,它有时会获得一些特殊的优化)。

但在大多数情况下,BigInteger足够快,无论如何都无关紧要。如果您真的认为这可能是一个问题,我建议您对代码进行分析并找出花费时间的事情。

如果添加BigIntegers确实是你的瓶颈,那么可能使用自定义可变大整数类作为累加器是有意义的。但在你证明这确实是主要的瓶颈之前,我不会这样做。