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

Java: Efficiently converting an array of longs to an array of bytes

我有一个 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[] 简单地视为一系列字节有效。

有趣的是,当读取文件时,使用缓冲区很容易"cast"从byte[]到longs:

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

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

我知道从 long 转换为 byte 时需要考虑字节序,但我相信我已经解决了这些问题:我正在使用上面显示的 Buffer 框架,默认为 big endian,无论本机字节顺序如何。

OP在这里。

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

但这看起来很深奥和令人费解,我觉得一定有更简单的方法。要么,要么我的方法有问题。

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

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

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

关于效率,许多细节实际上几乎没有什么不同。到目前为止,硬盘是这里涉及的最慢的部分,在将单个字节写入磁盘所花费的时间中,您可能已经将数千甚至数百万字节转换为长整型。这里的每个性能测试不会告诉你任何关于实现的性能,而是关于硬盘的性能。有疑问,应该做专门的benchmark比较不同的转换策略,分别比较不同的写法。

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

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

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

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();
    }
}

(当然,有人可能会争论分配 "large" 缓冲区是否是最好的主意。但是由于 Buffer 类 的便利方法,这可以很容易地和通过合理的努力修改为写入 "chunks" 具有适当大小的数据,对于真正想要写入 huge 数组和创建相应 数组的内存开销的情况=11=] 会太大)