我有一个longs
数组,我想写入磁盘。 最有效的磁盘 I/O 函数采用字节数组,例如:
FileOutputStream.write(byte[] b, int offset, int length)
。所以我想首先将我的long[]
转换为byte[]
(每long
8 个字节)。 我正在努力寻找一种干净的方法来做到这一点。
直接类型转换似乎不允许:
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
)。
但这似乎如此深奥和复杂,我觉得一定有更简单的方法。 要么是这样,要么是我方法中的某些东西是有缺陷的。