Java 为什么';MappedByteBuffer的array()方法是否有效?

Java 为什么';MappedByteBuffer的array()方法是否有效?,java,wolfram-mathematica,memory-mapped-files,Java,Wolfram Mathematica,Memory Mapped Files,我对Java非常陌生,尝试使用Mathematica的Java接口使用内存映射访问文件(希望性能有所提高) 我拥有的Mathematica代码(我相信)相当于以下Java代码(基于): 我想在缓冲区上使用array()方法,因此我尝试先使用load()将缓冲区内容加载到内存中。但是,即使在load()之后,isload()返回false,并且buffer.array()抛出异常:java.lang.UnsupportedOperationException 位于java.nio.ByteBuff

我对Java非常陌生,尝试使用Mathematica的Java接口使用内存映射访问文件(希望性能有所提高)

我拥有的Mathematica代码(我相信)相当于以下Java代码(基于):

我想在缓冲区上使用
array()
方法,因此我尝试先使用
load()
将缓冲区内容加载到内存中。但是,即使在
load()
之后,
isload()
返回
false
,并且
buffer.array()
抛出异常:
java.lang.UnsupportedOperationException
位于java.nio.ByteBuffer.array(ByteBuffer.java:940)

为什么不加载缓冲区?我如何调用
array()
方法

这里我的最终目标是使用
asDoubleBuffer().array()
获得一个
double
数组。方法
getDouble()
确实可以正常工作,但我希望能够一次性完成,以获得良好的性能。我做错了什么


当我在Mathematica中这样做时,我将发布我也使用过的Mathematica代码(相当于Java中的上述代码):

根据Javadoc
“映射字节缓冲区的内容可以随时更改,例如,如果映射文件的相应区域的内容被此程序或其他程序更改。这些更改是否发生以及何时发生取决于操作系统,因此未指定。”

映射字节缓冲区的全部或部分在任何时候都可能变得不可访问,例如,如果映射文件被截断。尝试访问映射字节缓冲区的不可访问区域将不会更改缓冲区的内容,并将导致在访问时或稍后某个时间引发未指定的异常。因此e强烈建议采取适当的预防措施,以避免本程序或同时运行的程序对映射文件进行操作,但读取或写入文件内容除外。”

在我看来,这似乎是许多条件和不良行为。你特别需要这门课吗

如果您只需要以最快的方式读取文件内容,请尝试:

FileChannel fChannel = new FileInputStream(f).getChannel();
    byte[] barray = new byte[(int) f.length()];
    ByteBuffer bb = ByteBuffer.wrap(barray);
    bb.order(ByteOrder.LITTLE_ENDIAN);
    fChannel.read(bb);
它的工作速度几乎等于磁盘系统测试速度

对于double,您可以使用DoubleBuffer(如果f.length()/4 size,则使用double[]数组),或者只调用ByteBuffer的getDouble(int)方法。

在Java中:

final byte[] hb;                  // Non-null only for heap buffers
因此,它甚至不是为MappedByteBuffer实现的,而是为HeapByteBuffer实现的

在Android中:

**
     * Child class implements this method to realize {@code array()}.
     *
     * @see #array()
     */
    abstract byte[] protectedArray();
同样,不是在MappedByteBuffer中,而是在例如ByteArrayBuffer中实现了支持数组

 @Override byte[] protectedArray() {
    if (isReadOnly) {
      throw new ReadOnlyBufferException();
    }
    return backingArray;
  }
内存映射的要点是离开堆。备份数组将位于堆上。
如果可以从RandomAccessFile打开FileChannel,然后在该通道上调用map,则还可以使用MappedByteBuffer上的bulk get()方法读取字节[]。这将避免IO,从堆外复制到堆内

buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
byte[] b = new byte[buffer.limit()];
buffer.get(b);

“返回值false并不一定意味着缓冲区的内容不在物理内存中。”
load
只是尽最大努力,实际上可能只是为了立即交换数据才将数据加载到物理内存中。@TomHawtin tackline我想我误解了
load
的目的。我想要实现的是将缓冲区的内容作为一个双精度数组。
array
方法unprotutanely不起作用,抛出了我提到的异常。我根据YOUR的反馈更新了问题。
array
仅适用于由数组支持的缓冲区(通常来自
*Buffer.wrap
)。@Szabolcs J/Link正在使用下面的MathLink进行操作。因此,通过J/Link将文件导入Mathematica不能比使用Mathlink更快,因为Mathlink本身会导致相当大的开销。如果我正确理解了问题的来源,那么主要的问题不是.mx文件的加载时间(我很难看到任何东西比.mx加载速度快),而是它们的粗粒度。这应该无关紧要,因为每个大的.mx文件只需要加载一次(在这种情况下,这种粗粒度就足够了)。如果没有,我会在更细粒度的.mx文件(它将作为大型.mx文件的“集群”)之上创建一个文件系统类型…@Szabolcs抽象层,这样我们就可以很好地近似于随机访问.mx文件,并避免重新读取不必要的数据。我想这是关键。对于大型数据文件(.mx或其他),我首先将其转换为这种“复合”格式(可能需要一段时间,但只需执行一次)。在那之后,onse应该能够像处理普通的随机访问文件一样处理它,而且速度应该很快。我并不特别需要这个类。我希望能够以一种比内置Mathematica功能更快的方式,将一个非常大的二进制文件(不是全部)的一部分内容作为一个double数组获取。更新的答案是“对于double,您可以使用DoubleBuffer(具有f.length()/4 size的double[]数组),或者只调用ByteBuffer的getDouble(int)方法。”你说过你只会读一些双打。我会使用ByteBuffer来避免可能将不需要的字节转换为双字节(在DoubleBuffer的情况下)。谢谢@andrey。我最终确实需要得到一个
double[]
,因为这是自动转换回Mathematica对象的东西(也就是说,我不能只使用带有一些索引的
getDouble
)。如果我试图直接读入一个
DoubleBuffer
,它就不起作用。如果我读入一个
ByteBuffer
,它确实起作用,我可以将它作为doublebuffer(),但是我通过这种方式获得的
hasArray()
再次返回
false
,也就是说,我不能将它转换为一个普通的
double
数组。您对如何在不显式循环w的情况下获得double数组有什么建议吗
 @Override byte[] protectedArray() {
    if (isReadOnly) {
      throw new ReadOnlyBufferException();
    }
    return backingArray;
  }
buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, fileChannel.size());
byte[] b = new byte[buffer.limit()];
buffer.get(b);