views:

939

answers:

2

I'm currently converting a Delphi method used for random binary file access into Java. The Delphi procedure uses:

TSingleArray  = Array[0..MAXSIZE] of Single
...
procedure GetLinkValues(const LinkVar: Integer;  const TimePeriod: Integer; var Value: PSingleArray);
...
BlockRead(Fout, Value^, Nlinks*SizeOf(Single));

To read an array of bytes into an array of Single. Is there an equivalent way of doing this in Java without iterating through the array?

I’m currently using

List<Float> l = new ArrayList<Float>();
…
for (int i = 0 ; i < nLinks ; i++ )
l.add( resultsFile.readFloat());

But I’m concerned about speed. Endianness is not a problem.

+2  A: 

Have you profiled the code and actually found it to be a problem? Something is going to have to loop... are you really sure this is a bottleneck in your code?

Having said all that, you should be able to use a FloatBuffer which I suspect does what you want. Unfortunately Sun's JavaDoc is down, so I can't easily link to or check the documentation at the minute.

To use a FloatBuffer, you'd probably want to:

  • Create a FileChannel associated with the file (e.g. with FileInputStream.getChannel)
  • Create a ByteBuffer
  • Create a FloatBuffer wrapping the ByteBuffer, with ByteBuffer.asFloatBuffer
  • Read into the ByteBuffer with FileChannel.read(byteBuffer)
  • Read from the FloatBuffer

I'm not particularly familiar/comfortable with java.nio, so I hope this is all correct - but it's likely to be pretty fiddly. Your current loop is almost certainly simpler, so I strongly suggest you check the performance of that first! You might want to wrap your current FileInputStream in a BufferedInputStream, btw.

Jon Skeet
Works for me, at least the third time I tried. http://java.sun.com/javase/6/docs/api/java/nio/FloatBuffer.html
Michael Myers
Still down for me. I think it must be a net split...
Jon Skeet
I think that means that the answer is no. What I was looking for was a way of replicating the pointer operation in Java, filling an array of Simple (float) with bytes as I believe (perhaps incorrectly) that this avoids iteration. Later I can read values using myArray[n]
GrahamA
@GrahamA: You can do block copies using a FloatBuffer, which will be efficient - but then the code will be more complicated. Basically you can have efficiency or simplicity, but not both. Have you tested the simple code for speed yet?
Jon Skeet
Profiled using System.currentTimeMillis() and a 65Mb file. Reading 27446 records. List<float> 140 MillisecondsByteBuffer 0Iteration through he list and byte buffer (after conversion to array) is 16ms. I have tried several different size results files and I’m getting consistent answers.
GrahamA
Are you making sure that you flush the file system cache first? Otherwise reading it the first time will hit disk and the second won't. Furthermore, is 16ms really a problem for you? Even if ByteBuffer is a lot faster, is this *definitely* a bottleneck for you?
Jon Skeet
A: 

Based on the help provided by Jon the final code looks like this:

    byte[] bt = new byte[nLinks * 4];
    List<Float> l = new ArrayList<Float>();
 if (linkVar != 0 && timePeriod < nPeriods ){
  long p1 = RECORDSIZE*(nNodes*NODEVARS + nLinks*LINKVARS);
  long p2 = RECORDSIZE*(nNodes*NODEVARS + nLinks*(linkVar-1));
  long p3 = offset2 + (timePeriod*p1) + p2;
  resultsFile.seek(p3);

  resultsFile.read( bt, 0, nLinks * 4);
  ByteBuffer bb = ByteBuffer.wrap(bt);
  bb.rewind();
  bb.asFloatBuffer().get(values);
    }
GrahamA