2011-12-21 11 views
9

Soy muy nuevo en Java y trato de utilizar la interfaz Java de Mathematica para acceder a un archivo usando la asignación de memoria (con la esperanza de una mejora en el rendimiento).¿Por qué no funciona el método array() de MappedByteBuffer?

El código de Mathematica que tengo es (creo) equivalente al siguiente código Java (basado en 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? 
    } 
} 

me gustaría usar el método array() en la memoria intermedia, por lo que estoy tratando de cargar el contenido de los búferes en la memoria primero usando load(). Sin embargo, incluso después de load(), isLoaded() devuelve false, y buffer.array() arroja una excepción: java.lang.UnsupportedOperationException at java.nio.ByteBuffer.array(ByteBuffer.java:940).

¿Por qué no carga el búfer y cómo puedo llamar al método array()?

Mi objetivo final aquí es obtener una matriz de double s usando asDoubleBuffer().array(). El método getDouble() funciona correctamente, pero esperaba hacerlo de una vez para obtener un buen rendimiento. ¿Qué estoy haciendo mal?


como lo estoy haciendo esto desde Mathematica, voy a publicar el código real Mathematica utilicé también (equivalente a la anterior en 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

"Un valor de retorno de falso no implica necesariamente que el contenido del búfer no reside en la memoria física." 'load' es solo un esfuerzo y, de hecho, puede haber cargado los datos en la memoria física solo para que se cancele inmediatamente. –

+0

@ TomHawtin-tackline Creo que entendí mal el propósito de 'cargar' en ese momento. Lo que me gustaría lograr es obtener los contenidos del buffer como una matriz de dobles. El método 'array' desafortunadamente no funciona, y arroja la excepción que mencioné. Actualicé la pregunta en función de tu opinión. – Szabolcs

+1

'array' solo funciona para los búferes que están respaldados por una matriz (normalmente de' * Buffer.wrap'). –

Respuesta

4

De acuerdo con Javadoc
"El El contenido de un búfer de bytes asignado puede cambiar en cualquier momento, por ejemplo, si este programa u otro cambian el contenido de la región correspondiente del archivo asignado. Si estos cambios ocurren y cuándo ocurren, depende del sistema operativo. y por lo tanto no especificado.

Todo o parte de un búfer de bytes asignado puede quedar inaccesible en cualquier momento, por ejemplo, si el archivo correlacionado se trunca. Un intento de acceder a una región inaccesible de un búfer de bytes mapeado no cambiará el contenido del búfer y provocará una excepción no especificada que se lanzará en el momento del acceso o en algún momento posterior. Por lo tanto, se recomienda encarecidamente que se tomen las precauciones adecuadas para evitar la manipulación de un archivo mapeado por este programa o por un programa que se ejecute simultáneamente, excepto para leer o escribir el contenido del archivo. "

A mi me parece . condiciones y el mal comportamiento no deseado ¿necesita particularmente esta clase

Si sólo tiene que leer el contenido del archivo en forma más rápida, darle una oportunidad:?

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

funciona a una velocidad casi igual a la velocidad de la prueba sistema de discos

Para el doble puede usar DoubleBuffer (con doble [] array si f.length()/4 size) o simplemente llame al método getDouble (int) de ByteBuffer.

+0

No necesito esta clase en particular. Tenía la esperanza de poder obtener el contenido de una parte de un archivo binario muy grande (no todo) como una matriz de dobles, de una manera más rápida que la que proporciona la funcionalidad incorporada de Mathematica. – Szabolcs

+0

Respuesta actualizada con "Para el doble puede usar DoubleBuffer (con doble [] matriz de tamaño f.length()/4) o simplemente llamar al método getDouble (int) de ByteBuffer." Dijiste que solo leerías algunos dobles. Usaría ByteBuffer para evitar la posible conversión de bytes innecesarios a dobles (en el caso de DoubleBuffer). – andrey

+0

Gracias @andrey. Necesito obtener un 'double []' eventualmente, ya que esto es lo que se convierte automáticamente a un objeto de Mathematica (es decir, no puedo usar 'getDouble' con algún índice). Si intento leer un 'DoubleBuffer' directamente, no funciona. Si leo en un 'ByteBuffer', funciona, y puedo obtener' asDoubleBuffer() ', pero lo que obtengo de esta manera devuelve' false' para 'hasArray()' de nuevo, es decir, no puedo convertirlo a una matriz simple de 'double's. ¿Tiene alguna sugerencia sobre cómo obtener una matriz de doble sin recorrer explícitamente todo y copiarlos uno por uno? – Szabolcs

0

en Java:

final byte[] hb;     // Non-null only for heap buffers 

por lo que ni siquiera se implementa para MappedByteBuffer pero es para HeapByteBuffer.

en Android:

** 
    * Child class implements this method to realize {@code array()}. 
    * 
    * @see #array() 
    */ 
    abstract byte[] protectedArray(); 

y de nuevo no en MappedByteBuffer, pero por ejemplo ByteArrayBuffer hace aplicar la matriz de soporte.

@Override byte[] protectedArray() { 
    if (isReadOnly) { 
     throw new ReadOnlyBufferException(); 
    } 
    return backingArray; 
    } 

El mapa de la memoria de punto está fuera de montón. Una matriz de respaldo estaría en montón.
Si puede abrir FileChannel desde RandomAccessFile y luego llamar al mapa en el canal, también puede usar el método bulk get() en MappedByteBuffer para leer en un byte []. Esto copia de montón, evitando IO, en montón otra vez.

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