2011-12-21 123 views
9

我對Java非常陌生,嘗試使用Mathematica的Java接口使用內存映射來訪問文件(希望能夠提高性能)。爲什麼MappedByteBuffer的array()方法不起作用?

Mathematica的代碼,我是(我相信)等同於Java代碼(基於this):

import java.io.FileInputStream; 
import java.nio.MappedByteBuffer; 
import java.nio.channels.FileChannel; 

public class MainClass { 
    private static final int LENGTH = 8*100; 

    public static void main(String[] args) throws Exception { 
    MappedByteBuffer buffer = new FileInputStream("test.bin").getChannel().map(FileChannel.MapMode.READ_ONLY, 0, LENGTH); 
    buffer.load(); 
    buffer.isLoaded(); // returns false, why? 
    } 
} 

我想用在緩衝區中的array()方法,所以我試圖加載首先使用load()將緩衝區內容存入內存。但是,即使在load(),isLoaded()返回false之後,並且buffer.array()也會拋出異常:java.lang.UnsupportedOperationException at java.nio.ByteBuffer.array(ByteBuffer.java:940)

爲什麼不緩衝加載,我怎樣才能調用array()方法?

我在這裏的最終目的是使用asDoubleBuffer().array()得到一組double。方法getDouble()的確可以正常工作,但我希望一舉取得這一成果,以獲得良好的性能。我究竟做錯了什麼?


當我從數學這樣做,我會後我用得(相當於上面在Java中)的實際數學代碼:

Needs["JLink`"] 
LoadJavaClass["java.nio.channels.FileChannel$MapMode"] 
buffer = JavaNew["java.io.FileInputStream", "test.bin"]@getChannel[]@map[FileChannel$MapMode`READUONLY, 0, 8*100] 

[email protected][] 
[email protected][] (* returns False *) 
+0

「返回值爲false並不一定意味着緩衝區的內容不駐留在物理內存中。」 'load'只是最好的努力,而且事實上可能已經將數據加載到物理內存中,僅僅是爲了立即換出。 – 2011-12-21 15:38:57

+0

@ TomHawtin-tackline我想我誤解了'load'的用途。我想實現的是將緩衝區的內容作爲一個雙精度數組來獲得。不幸的是'array'方法不起作用,並拋出我提到的異常。我根據你的反饋更新了這個問題。 – Szabolcs 2011-12-21 15:40:28

+1

'array'只適用於由數組支持的緩衝區(通常來自'* Buffer.wrap')。 – 2011-12-21 15:46:40

回答

4

根據的Javadoc
「的映射字節緩衝區的內容隨時都可能發生變化,例如,如果映射文件的相應區域的內容被此程序或其他內容改變,是否發生這種變化以及何時發生變化取決於操作系統因此未指定。

映射字節緩衝區的全部或部分可能在任何時候都變得不可訪問,例如,如果映射文件被截斷。嘗試訪問映射字節緩衝區的不可訪問區域將不會更改緩衝區的內容,並會導致在訪問時或稍後時間拋出未指定的異常。因此,強烈建議採取適當的預防措施以避免通過此程序或同時運行的程序對映射文件進行操作,但讀取或寫入文件內容除外。「

對我來說,好像很多。條件和不良行爲不端你需要特別此類

如果你只需要在最快的方式讀取文件內容,給一試:

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大小),或者只是調用ByteBuffer的getDouble(int)方法,則使用Double []數組。

+0

我不需要這個班。我希望能夠以比內置的Mathematica功能提供的更快的方式將一個非常大的二進制文件的一部分(不是全部)作爲一個雙精度數組來獲取。 – Szabolcs 2011-12-21 15:46:17

+0

已更新回答「對於double,您可以使用DoubleBuffer(使用f.length()/ 4大小的double []數組)或者只調用ByteBuffer的getDouble(int)方法。你說你只會讀一些雙打。我會使用ByteBuffer來避免將不需要的字節轉換爲雙精度(在DoubleBuffer的情況下)。 – andrey 2011-12-21 15:51:45

+0

謝謝@andrey。我最終需要得到一個'double []',因爲這是自動轉換回Mathematica對象的東西(即我不能只在某些索引中使用'getDouble')。如果我試圖直接讀入一個'DoubleBuffer',它不起作用。如果我讀入了'ByteBuffer',它確實可以工作,我可以將它作爲''asDoubleBuffer()',但是我以這種方式得到'hasArray()'的'false',也就是說我不能將它轉換爲一個普通的'double'數組。你有沒有對如何得到一個double數組沒有任何建議,而沒有明確地循環遍歷整個事物並將它們逐個拷貝出來? – Szabolcs 2011-12-21 16:06:06

0
在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上的批量get()方法讀入byte []。這從堆,避免IO,再次堆成堆。

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