Java位集和字节[]用法

Java位集和字节[]用法,java,bytearray,bitset,Java,Bytearray,Bitset,我有一个应用程序,我应该大量使用位集类,并逐位写入文件。我知道我不能将位写入文件,所以首先我将位集对象转换为字节数组,并作为字节数组写入。但问题是因为位集类从从右向左索引,当我将位集对象转换为字节数组并写入文件时,它会向后写入 例如,这是我的位集对象: 10100100 BitSet.get(0)给出false,BitSet.get(7)给出true。我想将此文件写入如下文件: 00100101 所以第一位是0,最后一位是1 我的转换方法: public static byte[] toBy

我有一个应用程序,我应该大量使用
位集
类,并逐位写入文件。我知道我不能将位写入文件,所以首先我将
位集
对象转换为字节数组,并作为字节数组写入。但问题是因为
位集
类从
从右向左
索引,当我将
位集
对象转换为字节数组并写入文件时,它会向后写入

例如,这是我的位集对象:

10100100
BitSet.get(0)给出false,BitSet.get(7)给出true。我想将此文件写入如下文件:

00100101
所以第一位是0,最后一位是1

我的转换方法:

public static byte[] toByteArray(BitSet bits) 
{
    byte[] bytes = new byte[(bits.length() + 7) / 8];       
    for (int i = 0; i < bits.length(); i++) {
        if (bits.get(i)) {
            bytes[bytes.length - i / 8 - 1] |= 1 << (i % 8);
        }
    }
    return bytes;
}

这是故意的还是我做错了什么?谢谢。

位集有几个问题:

  • 它使用
    .toByteArray()
    在输出时提供的字节数组的长度取决于设置为1的最高位(如果未设置位,则为0;如果最后一位设置为<8,则为1;如果<16,则为2,以此类推——本质上,
    indexOf(highestBitSet)+7)/8
  • 因此,您不能依赖它来计算固定长度的位掩码
考虑在
ByteBuffer
上使用包装器。下面是示例代码

注意:这使用“静态工厂方法”进行构造,因此您需要使用
位标志。WithByTeleLength()
位标志。withBitLength()
来创建新实例。当然,您可以为此设计自己的方法,或者将构造函数公开。要获取底层数组,请调用
.toByteArray()

公共最终类位标志
{
私有最终整数字节;
比特布弗私人决赛;
专用位标志(最终整数字节)
{
if(nrBytes<1)
抛出新的IllegalArgumentException(“至少需要一个字节”);
this.nrBytes=nrBytes;
buf=字节缓冲分配(nrBytes);
}
带有ByTeleLength(最终整数字节)的公共静态位标志
{
返回新的位标志(nrBytes);
}
带位长度的公共静态位标志(最终整数位)
{
返回新的位标志((nrBits-1)/8+1);
}
公共void setBit(最终整数位偏移)
{
if(位偏移量<0)
抛出新的IllegalArgumentException();
最终int byteToSet=位偏移量/8;
如果(byteToSet>nrBytes)
抛出新的IllegalArgumentException();
最终整数偏移量=位偏移量%8;
字节b=buf.get(byteToSet);
b |=1字节)
抛出新的IllegalArgumentException();
最终整数偏移量=位偏移量%8;
字节b=buf.get(byteToSet);

b&=~(1这在我看来是合理的。它不会很快,但应该可以工作。如果你想让它以相反的顺序写出位,只需反转索引和移位:

byte[] bytes = new byte[(bits.length() + 7) / 8];       
for (int i = 0; i < bits.length(); i++) {
    if (bits.get(i)) {
        bytes[i / 8] |= 1 << (7 - i % 8);
    }
}
如果您的位集相当稀疏(甚至可能不是),则仅迭代1位可能更有效:

byte[] bytes = new byte[(bits.length() + 7) / 8];
for ( int i = bits.nextSetBit(0); i >= 0; i = bits.nextSetBit(i+1) ) {
    bytes[i / 8] |= 128 >> (i % 8);
}
如果需要提高密集位集的速度,可以尝试使用标准方法,然后使用位旋转技巧反转各个字节中的位:

byte[] bytes = bits.toByteArray();
for ( int i = 0; i < bytes.length; i++ ) {
    byte b = bytes[i];
    b = ((b & 0x0F) << 4) | ((b & 0xF0) >> 4);
    b = ((b & 0x33) << 2) | ((b & 0xCC) >> 2);
    b = ((b & 0x55) << 1) | ((b & 0xAA) >> 1);
    bytes[i] = b;
}
byte[]bytes=bits.toByteArray();
for(int i=0;i4);
b=((b&0x33)>2);
b=((b&0x55)>1);
字节[i]=b;
}

位集实现可序列化。如果您只需要能够在Java中还原位集,而不需要检查其在文件中的状态,那么您应该告诉它将自身保存到文件中


如果希望将其写入包含其他非序列化数据的文件,可以将其写入ByteArrayOutputStream并检索字节[]但是,直接写入该文件可能会获得更好的性能。

如何使用该文件?您稍后会在java中读取该文件以还原位集吗?我将该文件读取为字节数组,并在需要时将其转换为位集,但使用外部二进制查看器时,它也会向后看。是的,这非常烦人。因此我创建了一个自定义位集t类,它扩展了位集类,并添加了一个数据字段(int)来保存位集的实际长度,因此现在我可以用错误的位值来开始和结束我的位集对象。但这不是问题所在。好吧,使用我的解决方案,您可以获得两个方面的最佳效果,因为您可以获得可靠的
字节[]
编写。正如我所说,如果您愿意,我可以提供示例代码。例如,我已经完成了在DNS头中设置标志的工作(标志部分在两个字节上)。谢谢!我会看一看!此应用程序将在智能手机和智能电视上运行,因此您认为仅对真实位进行迭代会更有效吗?我的位集是0的一半,1的一半以相等的间隔分布。可能是。有关替代方法,请参阅编辑。您可能希望对两者进行基准测试(以及其他答案中建议的解决方案)。
        bytes[i / 8] |= 128 >> (i % 8);
byte[] bytes = new byte[(bits.length() + 7) / 8];
for ( int i = bits.nextSetBit(0); i >= 0; i = bits.nextSetBit(i+1) ) {
    bytes[i / 8] |= 128 >> (i % 8);
}
byte[] bytes = bits.toByteArray();
for ( int i = 0; i < bytes.length; i++ ) {
    byte b = bytes[i];
    b = ((b & 0x0F) << 4) | ((b & 0xF0) >> 4);
    b = ((b & 0x33) << 2) | ((b & 0xCC) >> 2);
    b = ((b & 0x55) << 1) | ((b & 0xAA) >> 1);
    bytes[i] = b;
}