Использование Java BitSet и байт []
У меня есть это приложение, где я должен использовать класс BitSet
и записывать в файл по частям. Я знаю, что не могу записать биты в файл, поэтому сначала я преобразовываю объект BitSet
в массив байтов и пишу в качестве байтового массива. Но проблема в том, что класс BitSet
, индексированный из right to left
, когда я преобразовываю объект BitSet
в массив байтов и записываю в файл, он записывает обратную ссылку.
Например, это мой объект 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();
Предполагается ли это так, или я делаю что-то неправильно? Спасибо.
Ответы
Ответ 1
BitSet
имеет несколько проблем:
- длина массива байтов, который он предоставляет на выходе, с использованием
.toByteArray()
, зависит от самого верхнего бита, установленного в 1 (0, если бит не установлен, 1, если последний бит установлен равным < 8, 2, если < 16 и т.д. - по существу, indexOf(highestBitSet) + 7) / 8
);
- как таковой, вы не можете полагаться на него для вычисления битовой маски фиксированной длины.
Рассмотрим вместо этого вместо обложки ByteBuffer
. Пример кода ниже.
Примечание: для построения используется "статические методы factory", поэтому для создания нового экземпляра вам потребуется использовать либо 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();
}
}
Ответ 2
BitSet реализует Serializable. Если вам нужно только восстановить BitSet в Java и не нужно иначе проверять его состояние в файле, вы должны просто сказать ему, чтобы сохранить его в файле.
Если вы хотите записать его в файл, содержащий другие, несериализованные данные, вы можете записать его в ByteArrayOutputStream и извлечь из него байт []. Тем не менее, вы, вероятно, получите лучшую производительность, записывая непосредственно в файл.
Ответ 3
Это выглядит разумным для меня. Это будет не очень быстро, но это должно сработать. Если вы хотите, чтобы он записывал биты в обратном порядке, просто измените индексирование и сдвиг:
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;
}