早上看到一篇寫使用BigInteger計算階乘的文章,看了下源碼,有點小收穫。
BigInteger的主要內部數據:
int signum;
如果數值爲正,則signum爲1,值爲負則signum爲-1,值爲0則signum爲0。
int[] mag;
使用一個數組表示大數,例如: 如果大數用一個int就可以表示,則直接存這個數在mag[0]中,如果超出int的範圍,就擴充這個數組,存儲採用big endian方式。
簡單分析,把BigInteger中的stripLeadingZeroBytes()函數拷貝出來並調用:
public class A {
// Copy from the jdk 1.6 source code
private static int[] stripLeadingZeroBytes(byte a[]) {
int byteLength = a.length;
int keep;
// Find first nonzero byte
for (keep = 0; keep < a.length && a[keep] == 0; keep++)
;
// Allocate new array and copy relevant part of input array
int intLength = ((byteLength - keep) + 3) / 4;
int[] result = new int[intLength];
int b = byteLength - 1;
for (int i = intLength - 1; i >= 0; i--) {
result[i] = a[b--] & 0xff;
int bytesRemaining = b - keep + 1;
int bytesToTransfer = Math.min(3, bytesRemaining);
for (int j = 8; j <= 8 * bytesToTransfer; j += 8)
result[i] |= ((a[b--] & 0xff) << j);
}
return result;
}
public static void main(String[] args) {
byte x[] = new byte[5];
for (int i = 0; i < x.length; i++)
x[i] = 2;
System.out.println(Arrays.toString(stripLeadingZeroBytes(x)));
System.out.println(new BigInteger(x).toString(2));
}
}
output
=============
[2, 33686018]
1000000010000000100000001000000010
// 分開就是 10 00000010 00000010 00000010 00000010
可以看出,此時的mag長度是2,mag[0] 保存的是溢出位2(big endian)。
順便說一句,BigInteger和BigDecimal的作者是Bloch,都實現成他比較推崇的Immutable Object的形式,缺點是某些環境下可能產生性能問題。
=================================================
[b]new updated 2010.1.4 [/b]
[b]可能產生性能問題時的解決方案[/b]
Bloch在Effect Java中對不可變類由於生產對象過多而可能產生性能問題提出了兩種解決方案。
[b]方案一:在不可變類中對可能產生重複數據的情況提供方法實現[/b]
例如:考慮到可能使用BigInteger進行冪元素,在BigInteger中直接提供pow函數:
BigInteger中的pow的內部實現並不會產生多餘的BigInteger對象。但如果是個人使用BigInteger來實現x的n次方,即使使用平方後再相乘的方法,也會產生log2(N)個BigInteger對象,當n很大時,過多不必要的對象將佔有大量空間,如果來不及回收則會導致out of memory。
提供這種方法實現的很大一個好處就是域共用,即:新產生的BigInteger可以共用“舊”的BigInteger的部分域。典型的例子有:
// BigInteger的negate()方法:
public BigInteger negate() {...}
// 新產生的BigInteger和舊的BigInteger僅僅符號位不同,mag數組時共用的。
// BigInteger的setBit()方法:
public BigInteger setBit(int n)
// 用於置BigInteger的某一位爲1,新產生的BigInteger和舊的BigInteger僅僅在某個mag項不同。
缺點:不可能在不可變對象中提供所有可能的方法。
[b]方案二:爲不可變類提供一個相應的可變類[/b]
例如String是不可變類,StringBuilder是String的可變類。