Java:有效地将长整型数组转换为字节数组



我有一个longs数组,我想写入磁盘。 最有效的磁盘 I/O 函数采用字节数组,例如:

FileOutputStream.write(byte[] b, int offset, int length)

。所以我想首先将我的long[]转换为byte[](每long8 个字节)。 我正在努力寻找一种干净的方法来做到这一点。

直接类型转换似乎不允许:

ConversionTest.java:6: inconvertible types
found   : long[]
required: byte[]
byte[] byteArray = (byte[]) longArray;
^

通过遍历数组很容易进行转换,例如:

ByteBuffer bytes = ByteBuffer.allocate(longArray.length * (Long.SIZE/8));
for( long l: longArray )
{
bytes.putLong( l );
}
byte[] byteArray = bytes.array();

。然而,这似乎远不如简单地将 long[] 视为一系列字节的效率。

有趣的是,在读取文件时,使用 Buffers 很容易从byte[]"转换"到 longs:

LongBuffer longs = ByteBuffer.wrap(byteArray).asLongBuffer();

。但我似乎找不到任何相反方向的功能。

我知道从long转换为byte时有一些字节序注意事项,但我相信我已经解决了这些问题:我正在使用上面显示的 Buffer 框架,它默认为大端序,无论本机字节顺序如何。

不,没有一种简单的方法可以从long[]转换为byte[]

最好的选择可能是用BufferedOutputStream包装FileOutputStream,然后写出每个long的各个byte值(使用按位运算符)。

另一种选择是创建一个ByteBuffer并将long值放入ByteBuffer,然后将其写入FileChannel。这将为您处理字节序转换,但会使缓冲更加复杂。

关于效率,许多细节实际上几乎没有区别。到目前为止,硬盘是这里涉及的最慢的部分,在将单个字节写入磁盘所需的时间内,您可以将数千甚至数百万字节转换为长字节。这里的每个性能测试都不会告诉您有关实现性能的任何信息,而是有关硬盘性能的信息。有疑问的是,应该分别比较不同的转换策略和比较不同的写作方法,做专门的基准。

假设主要目标是允许方便转换并且不会施加不必要的开销的功能,我想提出以下方法:

可以创建一个足够大小的ByteBuffer,将其视为LongBuffer,使用批量LongBuffer#put(long[])方法(该方法负责必要的字节序转换,并尽可能有效地执行此操作),最后,将原始ByteBuffer(现在填充了long值)写入文件, 使用FileChannel.

按照这个想法,我认为这种方法很方便,而且(很可能)相当有效:

private static void bulkAndChannel(String fileName, long longArray[]) 
{
ByteBuffer bytes = 
ByteBuffer.allocate(longArray.length * Long.BYTES);
bytes.order(ByteOrder.nativeOrder()).asLongBuffer().put(longArray);
try (FileOutputStream fos = new FileOutputStream(fileName))
{
fos.getChannel().write(bytes);
}
catch (IOException e)
{
e.printStackTrace();
}
}

(当然,人们可能会争论分配"大"缓冲区是否是最好的主意。但是由于Buffer类的便利方法,这可以很容易地通过合理的努力进行修改,以写入具有适当大小的数据"块",因为人们真的想写一个巨大的数组,并且创建相应ByteBuffer的内存开销将大得令人望而却步)

OP 在这里。

我想到一种方法:ByteBuffer.asLongBuffer()返回一个ByteBufferAsLongBufferB的实例,一个将 ByteBuffer 包装在接口中的类,用于将数据视为long,同时正确管理字节序。我可以扩展ByteBufferAsLongBufferB,并添加一个方法来返回原始字节缓冲区(即protected)。

但这似乎如此深奥和复杂,我觉得一定有更简单的方法。 要么是这样,要么是我方法中的某些东西是有缺陷的。

最新更新