12

BitSetクラスを多用し、ファイルに少しずつ書き込む必要があるこのアプリケーションがあります。ファイルにビットを書き込めないことはわかっているので、まずBitSetオブジェクトをバイト配列に変換し、バイト配列として書き込みます。しかし、問題は、オブジェクトをバイト配列に変換してファイルに書き込むと、BitSetクラスが からインデックス付けされるため、逆方向に書き込まれることです。right to leftBitSet

たとえば、これは私の BitSet オブジェクトです:

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;
}

私の書き込み方法:

    FileOutputStream fos = new FileOutputStream(filePath);
    fos.write(BitOperations.toByteArray(cBitSet));
    fos.close();

これはこのようにすることを意図したものですか、それとも何か間違ったことをしていますか? ありがとうございました。

4

3 に答える 3

6

BitSetいくつかの問題があります:

  • を使用して出力で提供するバイト配列の長さは、.toByteArray()1 に設定された最上位ビットに依存します (ビットが設定されていない場合は 0、最後のビットが設定されている場合は 1、< 16 の場合は 2 など - 本質的にはindexOf(highestBitSet) + 7) / 8);
  • そのため、固定長のビット マスクの計算にこれを使用することはできません。

ByteBuffer代わりにラッパーを使用することを検討してください。以下サンプルコード。

注: これは構築に「静的ファクトリ メソッド」を使用するため、 または のいずれかを使用して新しいインスタンスを作成する必要がありBitFlags.withByteLength()ますBitFlags.withBitLength()。もちろん、そのための独自のメソッドを考案したり、コンストラクターを公開したりすることもできます。基になる配列を取得するには、 を呼び出します.toByteArray()

public final class BitFlags
{
    private final int nrBytes;
    private final ByteBuffer buf;

    private BitFlags(final int nrBytes)
    {
        if (nrBytes < 1)
            throw new IllegalArgumentException("need at least one byte");
        this.nrBytes = nrBytes;
        buf = ByteBuffer.allocate(nrBytes);
    }

    public static BitFlags withByteLength(final int nrBytes)
    {
        return new BitFlags(nrBytes);
    }

    public static BitFlags withBitLength(final int nrBits)
    {
        return new BitFlags((nrBits - 1) / 8 + 1);
    }

    public void setBit(final int bitOffset)
    {
        if (bitOffset < 0)
            throw new IllegalArgumentException();

        final int byteToSet = bitOffset / 8;
        if (byteToSet > nrBytes)
            throw new IllegalArgumentException();

        final int offset = bitOffset % 8;
        byte b = buf.get(byteToSet);
        b |= 1 << offset;
        buf.put(byteToSet, b);
    }

    public void unsetBit(final int bitOffset)
    {
        if (bitOffset < 0)
            throw new IllegalArgumentException();

        final int byteToSet = bitOffset / 8;
        if (byteToSet > nrBytes)
            throw new IllegalArgumentException();

        final int offset = bitOffset % 8;
        byte b = buf.get(byteToSet);
        b &= ~(1 << offset);
        buf.put(byteToSet, b);
    }

    public byte[] toByteArray()
    {
        return buf.array();
    }
}
于 2012-12-25T17:09:44.947 に答える
6

BitSet は Serializable を実装します。Java で BitSet を復元できるようにする必要があるだけで、ファイル内の状態を調べる必要がない場合は、それ自体をファイルに保存するように指示する必要があります。

シリアル化されていない他のデータを含むファイルに書き込みたい場合は、それを ByteArrayOutputStream に書き込み、そこから byte[] を取得できます。ただし、ファイルに直接書き込む方がパフォーマンスが向上する可能性があります。

于 2012-12-25T17:21:04.707 に答える
2

それは私には合理的に見えます。それほど速くはありませんが、うまくいくはずです。逆の順序でビットを書き出す場合は、インデックスとシフトを逆にするだけです。

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);
    }
}

あるいは:

        bytes[i / 8] |= 128 >> (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);
}

高密度のビットセットでさらに速度が必要な場合は、標準的な方法を試してから、ビット調整のトリックを使用して個々のバイトのビットを逆にすることができます。BitSet.toByteArray()

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;
}
于 2012-12-25T17:10:41.130 に答える