大多数与字节相关的操作都会自动提升为整型。例如,考虑一个简单的方法,它将一个字节常量添加到每个元素中的
byte[]
数组,并返回一个新的
byte[]
数组(可能是
ByteStream
的候选对象):
public static byte[] add(byte[] arr, byte addend) {
byte[] result = new byte[arr.length];
int i=0;
for(byte b : arr) {
result[i++] = (byte) (b+addend);
}
return result;
}
看,即使我们对两个
byte
变量执行加法,它们也会被扩展为
int
,你需要将结果强制转换回
byte
。在 Java 字节码中,除了数组加载/存储和强制转换为字节之外,大多数与
byte
相关的操作(
iadd
、
ixor
、
if_icmple
等)都使用 32 位整数指令表示。因此,实际上可以使用
IntStream
处理字节作为整数。我们只需要两个额外的操作:
- 从
byte[]
数组创建一个 IntStream
(将字节扩展为整数)
- 将
IntStream
收集到 byte[]
数组中(使用 (byte)
转换)
第一个操作非常简单,可以像这样实现:
public static IntStream intStream(byte[] array) {
return IntStream.range(0, array.length).map(idx -> array[idx]);
}
所以你可以在你的项目中添加这样的静态方法并且感到高兴。
将流收集到byte[]
数组中更加棘手。使用标准JDK类的最简单解决方案是ByteArrayOutputStream
:
public static byte[] toByteArray(IntStream stream) {
return stream.collect(ByteArrayOutputStream::new, (baos, i) -> baos.write((byte) i),
(baos1, baos2) -> baos1.write(baos2.toByteArray(), 0, baos2.size()))
.toByteArray();
}
然而,由于同步的不必要开销,它存在不必要的开销。此外,为了减少分配和复制,特别处理已知长度的流会更好。尽管如此,现在您可以将Stream API用于
byte []
数组:
public static byte[] addStream(byte[] arr, byte addend) {
return toByteArray(intStream(arr).map(b -> b+addend));
}
我的 StreamEx 库在 IntStreamEx
类中提供了这两个操作,它增强了标准的 IntStream
,因此您可以像这样使用它:
public static byte[] addStreamEx(byte[] arr, byte addend) {
return IntStreamEx.of(arr).map(b -> b+addend).toByteArray();
}
toByteArray()
方法在内部使用简单可调整大小的 字节缓冲区,并且 特别处理 当流是连续的且目标大小已知的情况。