【问题标题】:writing a BitSet to a file in java在java中将BitSet写入文件
【发布时间】:2025-12-22 13:05:13
【问题描述】:

我有一个 BitSet,想将它写入文件 - 我遇到了一个使用 writeObject 方法使用 ObjectOutputStream 的解决方案。

我查看了 java API 中的 ObjectOutputStream,发现您可以编写其他内容(字节、int、short 等)

我尝试检查该类,因此我尝试使用以下代码将一个字节写入文件,但结果给了我一个 7 字节而不是 1 字节的文件

我的问题是文件中的前 6 个字节是什么?他们为什么在那里?

我的问题与 BitSet 相关,因为我不想开始将大量数据写入文件并意识到我在文件中插入了随机字节而不知道它们是什么。

代码如下:

    byte[] bt = new byte[]{'A'};
    File outFile = new File("testOut.txt");
    FileOutputStream fos = new FileOutputStream(outFile);
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.write(bt);
    oos.close();

感谢您的帮助

Avner

【问题讨论】:

    标签: java bitset objectoutputstream


    【解决方案1】:

    其他字节将是类型信息。

    基本上 ObjectOutputStream 是一个用于将可序列化对象写入某个目标(通常是文件)的类。如果您考虑 InputObjectStream,它会更有意义。它有一个 readObject() 方法。 Java如何知道要实例化什么对象?简单:里面有类型信息。

    【讨论】:

    • 所以如果我理解正确 - 每次我使用 ObjectOutputStream 写东西时,每次写都会有严重的开销。例如,如果我写一个 int、一个short、一个字节然后是一个字符串,我会为我写的每个项目获得 4 组额外数据?
    • 没有。只有 writeObject() 方法会添加类型标头。 writeUTF() 方法添加一个 2 字节长度的前缀。原始的 writeXX() 方法不会增加任何开销。阅读 API 文档了解详情。
    • 还要注意类型信息是per-Object的。对于基本上由原始数组(如 BitSet)组成的对象,无论数组有多大,开销都是恒定的。
    【解决方案2】:

    您可以将任何对象写入ObjectOutputStream,因此流包含有关写入类型的信息以及重构对象所需的数据。

    如果您知道流将始终包含一个 BitSet,请不要使用 ObjectOutputStream - 如果空间很重要,则将 BitSet 转换为一组字节,其中每个位对应于BitSet,然后将其直接写入底层流(例如,FileOutputStream,如您的示例所示)。

    【讨论】:

    • 不幸的是,BitSet 没有内置方法将其转换为字节数组。
    • 有方法:toByteArray()
    • @clankill3r:是的,还有toLongArray(),但仅从Java 7开始。
    【解决方案3】:

    与许多其他格式一样,序列化格式包括带有幻数和版本信息的标头。当您使用DataOutput/OutputStream 时,ObjectOutputStream 上的方法被放置在序列化数据的中间(没有类型信息)。这通常仅在调用defaultWriteObject 或使用putFields 后在writeObject 实现中完成。

    【讨论】:

      【解决方案4】:

      如果您只使用 Java 中保存的 BitSet,则序列化工作正常。但是,如果您想跨多个平台共享 bitset,那就有点烦人了。除了 Java 序列化的开销之外,BitSet 以 8 字节为单位存储。如果您的位集很小,这可能会产生过多的开销。

      我们编写了这个小类,以便我们可以从 BitSet 中提取字节数组。根据您的用例,它可能比 Java 序列化更适合您。

      public class ExportableBitSet extends BitSet {
      
          private static final long serialVersionUID = 1L;
      
          public ExportableBitSet() {
              super();
          }
      
          public ExportableBitSet(int nbits) {
              super(nbits);
          }
      
          public ExportableBitSet(byte[] bytes) {
              this(bytes == null? 0 : bytes.length*8);        
              for (int i = 0; i < size(); i++) {
                  if (isBitOn(i, bytes))
                      set(i);
              }
          }
      
          public byte[] toByteArray()  {
      
              if (size() == 0)
                  return new byte[0];
      
              // Find highest bit
              int hiBit = -1;
              for (int i = 0; i < size(); i++)  {
                  if (get(i))
                      hiBit = i;
              }
      
              int n = (hiBit + 8) / 8;
              byte[] bytes = new byte[n];
              if (n == 0)
                  return bytes;
      
              Arrays.fill(bytes, (byte)0);
              for (int i=0; i<n*8; i++) {
                  if (get(i)) 
                      setBit(i, bytes);
              }
      
              return bytes;
          }
      
          protected static int BIT_MASK[] = 
              {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
      
          protected static boolean isBitOn(int bit, byte[] bytes) {
              int size = bytes == null ? 0 : bytes.length*8;
      
              if (bit >= size) 
                  return false;
      
              return (bytes[bit/8] & BIT_MASK[bit%8]) != 0;
          }
      
          protected static void setBit(int bit, byte[] bytes) {
              int size = bytes == null ? 0 : bytes.length*8;
      
              if (bit >= size) 
                  throw new ArrayIndexOutOfBoundsException("Byte array too small");
      
              bytes[bit/8] |= BIT_MASK[bit%8];
          }
      }
      

      【讨论】: