Java从BigInteger到位集再返回

Java从BigInteger到位集再返回,java,bitset,bigint,Java,Bitset,Bigint,在Java8中,下面的代码将整数3转换为位集,并打印{0,1},这意味着3的位表示在0和1位置有一个位,即11 System.out.println(BitSet.valueOf(new long[]{3})); 我感兴趣的是将一个具有大值的biginger或long转换为相应的位集,然后返回-让位集对象(位表示)将其转换为biginger(long)。我还想知道,对于各种整数值,哪个表示法占用更多内存?您可以使用and来表示这些值: BigInteger bi = new BigIntege

Java8
中,下面的代码将整数
3
转换为位集,并打印
{0,1}
,这意味着
3
的位表示在
0
1
位置有一个位,即
11

System.out.println(BitSet.valueOf(new long[]{3}));
我感兴趣的是将一个具有大值的
biginger
long
转换为相应的
位集
,然后返回-让
位集
对象(位表示)将其转换为
biginger
long
)。我还想知道,对于各种整数值,哪个表示法占用更多内存?

您可以使用and来表示这些值:

BigInteger bi = new BigInteger("31415926535");
bi = bi.multiply(new BigInteger("271828"));
System.out.println(bi);
BitSet bs = BitSet.valueOf(bi.toByteArray());
System.out.println(bs);
BigInteger bi2 = new BigInteger(bs.toByteArray());
System.out.println(bi2);
您可以使用的构造函数将字节数组转换为
biginger
,并使用将数据转换为所需的值

对于给定的代码,它输出:

8539728478155980
{1, 2, 3, 4, 9, 10, 12, 14, 17, 18, 20, 22, 23, 25, 27, 28, 29, 30, 32, 33, 35, 37, 38, 42, 44, 45, 50, 51, 54, 55}
8539728478155980
请注意,使用了2-补码表示法。因此,对于负数,它将生成额外的负数。而
2^64-1
将需要超过
64
位来表示。这也适用于big-endian。(见下面修改的答案)

编辑:但是,这种方法存在一个问题:如果存在尾随零,则程序不会考虑这些尾随零。这可能很重要,因为它们是表示的一部分。您可以通过添加尾随钻头来解决此问题:

public static BitSet convertTo (BigInteger bi) {
    byte[] bia = bi.toByteArray();
    int l = bia.length;
    byte[] bsa = new byte[l+1];
    System.arraycopy(bia,0,bsa,0,l);
    bsa[l] = 0x01;
    return BitSet.valueOf(bsa);
}

public static BigInteger convertFrom (BitSet bs) {
    byte[] bsa = bs.toByteArray();
    int l = bsa.length-0x01;
    byte[] bia = new byte[l];
    System.arraycopy(bsa,0,bia,0,l);
    return new BigInteger(bia);
}
并称之为:

BigInteger bi = new BigInteger("20000000");
System.out.println(bi);

BitSet bs = convertTo(bi);
System.out.println(bs);

BigInteger bi2 = convertFrom(bs);
System.out.println(bi2);
关于内存使用情况


通常,这两种方法将使用大致相同的位数。对于第一种实现(没有大小标记,因此也没有错误),有时
位集
方法使用的字节可能比
biginger
方法少一个字节。对于大小标记(第二种方法),情况正好相反:一般来说,
位集
将始终使用一个额外的字节,少数情况除外。

因为
biginger
是大端数,而
位集
是小端数,当试图通过
toByteArray()
从一个字节直接转换到另一个字节时,字节将被反转。最简单的解决方案是再次将其反转:

/**
 * Converts from BigInteger to BitSet. The BigInteger must be non-negative,
 * otherwise an IllegalArgumentException is thrown.
 */
public static BitSet toBitSet(BigInteger val) {
    if(val.signum() < 0)
        throw new IllegalArgumentException("Negative value: " + val);
    return BitSet.valueOf(reverse(val.toByteArray()));
}
/**
 * Converts from BitSet to BigInteger.
 */
public static BigInteger toBigInteger(BitSet val) {
    return new BigInteger(1, reverse(val.toByteArray()));
}
/**
 * Reverses and returns the specified byte array.
 */
private static byte[] reverse(byte[] bytes) {
    for(int i = 0; i < bytes.length/2; i++) {
        byte temp = bytes[i];
        bytes[i] = bytes[bytes.length-i-1];
        bytes[bytes.length-i-1] = temp;
    }
    return bytes;
}
/**
*从BigInteger转换为位集。BigInteger必须是非负的,
*否则将抛出IllegalArgumentException。
*/
公共静态位集toBitSet(biginger val){
if(val.signum()<0)
抛出新的IllegalArgumentException(“负值:+val”);
返回BitSet.valueOf(反向(val.toByteArray());
}
/**
*将位集转换为BigInteger。
*/
公共静态BigInteger TobiInteger(位集val){
返回新的BigInteger(1,反向(val.toByteArray());
}
/**
*反转并返回指定的字节数组。
*/
专用静态字节[]反向(字节[]字节){
对于(int i=0;i

顺便说一下,如果您只对位索引感兴趣,您可以使用。

它不起作用。(Endianness是向后的。)试试看,你就会明白我的意思。@Radiodef:你是说它可以和big endian一起工作?显然,否则怎么能表示任意大的数字?
biginger#toByteArray
返回一个大端数组,
BitSet
需要一个小端数组。检查文档。它适用于最大值为“20000000”的整数,而不适用于其他整数。有可能用于更大的整数吗?@StephanRozinsky:你能编辑你的问题并给出一些测试用例吗?这可能与endianness有关,但你必须更具体地描述你对大数字的期望。如您所见,对于较大的值
8e16
,它仍然保持数字…另外一句话:endianness很重要,因为这是一个重要方面<代码>3
现在表示为
{0,1}
。因此,
i
-th位采用
2^i
作为值。这仅反转字节顺序(8位组)。位的顺序也必须颠倒。所有这些都需要所需结果的精确长度,以适应任何拖尾zeros@Radu西蒙涅斯库:这是错误的。位集使用小字节顺序(如果从字节[]加载,则与大字节顺序相反)。在处理bigInteger和位集之间的位时,需要相反的顺序,请对此答案向上投票。