在 Java 中,是否有内置或常见的 Stream 允许输出,比如说,5 位数据?
In Java, is there a built-in or common kind of Stream that allows outputting, say, 5-bit data?
现在我有一个小程序试图有效地将消息分成几部分,计算需要将字符单独附加到 OutputStream
,通常是 BAOS
,然后执行类似 byte[] packed = packData(baos)
并根据打包大小计算大小。这个打包步骤是必要的,因为我在 baos.write(my5bitbyte)
.
时浪费了一些位
所以在打包步骤中,我通常会这样做:
- 从
baos.toByteArray()
的字节中取出一个位集
- 为构造字节设置新位
- 从每个字节中取出位 0-4 并以明显的方式将它们附加到新的位集
- 从新的位集创建一个
byte[]
,填充最后一个字节的最后 7 位
我的问题是:
有什么方法或类似 BitOutputStream
的东西吗?或者类似的东西?我目前这样做的方式似乎很愚蠢,我肯定可以更聪明,但我想知道我是否忽略了一些已经存在的东西。
编辑 在检查了 ByteArrayOutputStream
的来源后,很明显它可以用完全相同的方式对某些 BitArrayOutputStream
实施,因为它是只是一个 byte[]
封装了一些花哨的东西,所以你可以做 boolean[]
。但我认为它不存在,现在我进一步研究它,所以我的问题就变成了......
那么这是实现 BitArrayOutputStream
的合理方式吗?
class FixedLengthBitArrayOutputStream extends OutputStream {
private boolean[][] buffer;
private final int originalLength;
private final int bitLength;
private int position = 0;
private int expansions = 0;
FixedLengthBitArrayOutputStream(short bitLength, short length) {
this.buffer = new boolean[length][bitLength];
this.originalLength = length;
this.bitLength = bitLength;
}
private int limitBeforeExpansion(double factor) {
return Math.max(
(int) Math.floor(factor * buffer.length),
(int) Math.floor( (1 - Math.pow(factor, expansions + 1)) * buffer.length)
);
}
private boolean needsExpansion() {
return position > limitBeforeExpansion(0.8);
}
private void expandIfNecessary() {
if (needsExpansion()) {
expansions++;
this.buffer = Arrays.copyOf(this.buffer, (int) Math.pow((double)this.originalLength, expansions + 1));
}
}
public boolean[] bitValue(int number) throws IllegalStateException {
int remainder = number;
boolean[] bits = new boolean[this.bitLength];
for (int i = this.bitLength - 1; i >= 0; i--) {
int power = (int) Math.pow(2, i + 1);
boolean value = remainder > power;
bits[i] = value;
if (value) {
remainder -= power;
}
}
if (remainder != 0)
throw new IllegalStateException("whoa");
return bits;
}
@Override
public void write(int b) throws IOException, IllegalStateException {
expandIfNecessary();
this.buffer[position] = bitValue(b);
position++;
}
public byte[] toByteArray() {
BitSet bitSet = new BitSet(this.position * this.bitLength);
for (int i = 0; i < position; i++) {
boolean[] bits = this.buffer[i];
for (int j = 0; j < bits.length; j++) {
bitSet.set( i * bits.length + j , bits[j] );
}
}
return bitSet.toByteArray();
}
}
一种自然的方法是编写一个单独的输出流 class 链接(或包装)另一个输出流(与链接写入器、缓冲流和非缓冲流的方式相同)。
代码可能与此类似。我缓冲了几位直到它达到一个完整的字节并将其写入输出流。我还没有测试过。因此它可能包含一两个错误。
class PackedBitsOutputStream {
private OutputStream outputStream;
private int numBufferedBits;
private byte bufferedBits;
PackedBitsOutputStream(OutputStream os) {
outputStream = os;
}
void writeBitSet(int data, int relevantBits) {
bufferedBits = (byte) (bufferedBits | (data << bufferedBits));
numBufferedBits += relevantBits;
if (numBufferedBits >= 8) {
outputStream.write(bufferedBits);
numBufferedBits -= 8;
bufferedBits = (byte) (data >> (relevantBits - numBufferedBits));
}
}
void flush() {
outputStream.write(bufferedBits);
bufferedBits = 0;
numBufferedBits = 0;
outputStream.flush();
}
void close() {
flush();
outputStream.close();
}
}
注意:writeBitSet目前一次最多可以写入8位。
现在我有一个小程序试图有效地将消息分成几部分,计算需要将字符单独附加到 OutputStream
,通常是 BAOS
,然后执行类似 byte[] packed = packData(baos)
并根据打包大小计算大小。这个打包步骤是必要的,因为我在 baos.write(my5bitbyte)
.
所以在打包步骤中,我通常会这样做:
- 从
baos.toByteArray()
的字节中取出一个位集
- 为构造字节设置新位
- 从每个字节中取出位 0-4 并以明显的方式将它们附加到新的位集
- 从新的位集创建一个
byte[]
,填充最后一个字节的最后 7 位
我的问题是:
有什么方法或类似 BitOutputStream
的东西吗?或者类似的东西?我目前这样做的方式似乎很愚蠢,我肯定可以更聪明,但我想知道我是否忽略了一些已经存在的东西。
编辑 在检查了 ByteArrayOutputStream
的来源后,很明显它可以用完全相同的方式对某些 BitArrayOutputStream
实施,因为它是只是一个 byte[]
封装了一些花哨的东西,所以你可以做 boolean[]
。但我认为它不存在,现在我进一步研究它,所以我的问题就变成了......
那么这是实现 BitArrayOutputStream
的合理方式吗?
class FixedLengthBitArrayOutputStream extends OutputStream {
private boolean[][] buffer;
private final int originalLength;
private final int bitLength;
private int position = 0;
private int expansions = 0;
FixedLengthBitArrayOutputStream(short bitLength, short length) {
this.buffer = new boolean[length][bitLength];
this.originalLength = length;
this.bitLength = bitLength;
}
private int limitBeforeExpansion(double factor) {
return Math.max(
(int) Math.floor(factor * buffer.length),
(int) Math.floor( (1 - Math.pow(factor, expansions + 1)) * buffer.length)
);
}
private boolean needsExpansion() {
return position > limitBeforeExpansion(0.8);
}
private void expandIfNecessary() {
if (needsExpansion()) {
expansions++;
this.buffer = Arrays.copyOf(this.buffer, (int) Math.pow((double)this.originalLength, expansions + 1));
}
}
public boolean[] bitValue(int number) throws IllegalStateException {
int remainder = number;
boolean[] bits = new boolean[this.bitLength];
for (int i = this.bitLength - 1; i >= 0; i--) {
int power = (int) Math.pow(2, i + 1);
boolean value = remainder > power;
bits[i] = value;
if (value) {
remainder -= power;
}
}
if (remainder != 0)
throw new IllegalStateException("whoa");
return bits;
}
@Override
public void write(int b) throws IOException, IllegalStateException {
expandIfNecessary();
this.buffer[position] = bitValue(b);
position++;
}
public byte[] toByteArray() {
BitSet bitSet = new BitSet(this.position * this.bitLength);
for (int i = 0; i < position; i++) {
boolean[] bits = this.buffer[i];
for (int j = 0; j < bits.length; j++) {
bitSet.set( i * bits.length + j , bits[j] );
}
}
return bitSet.toByteArray();
}
}
一种自然的方法是编写一个单独的输出流 class 链接(或包装)另一个输出流(与链接写入器、缓冲流和非缓冲流的方式相同)。
代码可能与此类似。我缓冲了几位直到它达到一个完整的字节并将其写入输出流。我还没有测试过。因此它可能包含一两个错误。
class PackedBitsOutputStream {
private OutputStream outputStream;
private int numBufferedBits;
private byte bufferedBits;
PackedBitsOutputStream(OutputStream os) {
outputStream = os;
}
void writeBitSet(int data, int relevantBits) {
bufferedBits = (byte) (bufferedBits | (data << bufferedBits));
numBufferedBits += relevantBits;
if (numBufferedBits >= 8) {
outputStream.write(bufferedBits);
numBufferedBits -= 8;
bufferedBits = (byte) (data >> (relevantBits - numBufferedBits));
}
}
void flush() {
outputStream.write(bufferedBits);
bufferedBits = 0;
numBufferedBits = 0;
outputStream.flush();
}
void close() {
flush();
outputStream.close();
}
}
注意:writeBitSet目前一次最多可以写入8位。