views:

130

answers:

2

I have a file that contains about 200,000 long values that I want to read as fast as possible into a long[]. It's for an Android app; function calls are slow (so anything involving reading a long at a time with a "for" loop will be super slow) and I need loading to be fast. What can I use? Everything I look at seems to read only bytes fast.

I've used ByteBuffer and FileChannel from the NIO package before and this seems a really fast way to load arrays of values from files. However, I cannot work out how to use this to read data into a long[]. I've tried wrapping a long[] as a LongBuffer, but I cannot see any way I can feed data from a file into a LongBuffer.

Edit: Whatever method I use, I need to be able to use Arrays.binarySearch on the long[] array at the end.

A: 

There is no way to cast a byte[] into a long[]. However, you could try to use a FileChannel to read the content into a ByteBuffer and then get hold of a LongBuffer through ByteBuffer.asLongBuffer from which you could get a long[] through LongBuffer.array().

You could also try FileChannel.map to get a MappedByteBuffer of the file. This may be faster than going through a FileChannel.read.

If this doesn't work you could try to use a FileChannel to read the content into a ByteBuffer and then access the longs inside it, using ByteBuffer.getLong(index).


An alternative solution. (No method-calls in the loop :-)

byte[] byteArray = new byte[longCount * 8];
FileInputStream fis = new FileInputStream("lotsoflongs");
fis.read(byteArray);
fis.close();
for (int i = 0; i < longCount; i += 8)
    longArray[i >> 3] = ((long) byteArray[0+i]        << 56) +
                        ((long)(byteArray[1+i] & 255) << 48) +
                        ((long)(byteArray[2+i] & 255) << 40) +
                        ((long)(byteArray[3+i] & 255) << 32) +
                        ((long)(byteArray[4+i] & 255) << 24) +
                              ((byteArray[5+i] & 255) << 16) +
                              ((byteArray[6+i] & 255) <<  8) +
                              ((byteArray[7+i] & 255) <<  0);

I've benchmarked a few solutions now, and this one seems to be the fastest way of doing it. Also, note that the actual bytes read in fis.read(byteArray) may be less than the actual size of byteArray. Thus, if this should be done properly, you need to put it in a loop that iterates until all bytes have been read.

aioobe
I need to be able to use the long[] like a long[] after though e.g. using it with Arrays.binarySearch
memcom
How about implementing a `List` of longs, backed by a byte-array, and use Collections.binarySearch?
aioobe
I'm not sure what you mean by this? How would I create such a List object?
memcom
Like this: `class MyList implements java.util.List { byte[] backingArray; MyList(byte[] data) { backingArray = data; } /* all list methods you need */ }`
aioobe
Hmm. Is there no easier way than having to implement a whole custom class? I'm confused why this is so hard to do in Java. :(
memcom
Updated my answer. Have you had a look at `ByteBuffer.asLongBuffer` ?
aioobe
" ByteBuffer.asLongBuffer from which you could get a long[] through LongBuffer.array()", I've actually tried this but .array() will throw an exception. I think this is because there is no long[] to return when the wrapped array was byte[]
memcom
Ah, I see it too now. Updated the answer with an alternative solution. Try it out. It seems quite fast in my tests.
aioobe
A couple points. First, fis.read(byteArray) may return a short count and not read the entire array. Second, don't the byteArray[n] accesses in the loop always extract the same (first) long - should they not be byteArray[i+n]?
Software Monkey
oh. good points @software monkey :-) copy-paste-error.
aioobe
That's faster than using DataInputStream's getLong() method? Mind to post your results with different methods you tested? ^^
Tseng
+1  A: 

Try using a DataInputStream. Since you can easily find out the length of the file, you also know how many elements it contains (filesize / 8 byte).

 DataInputStream dataStream = new DataInputStream(inputStream);

 long count = filesize/8;
 long[] longArray = new long[count];

 for(int i=0;i<count;i++) 
     longArray[i] = dataStream.getLong();

Guess that should be enough to give you an idea.

Tseng
The OP ruled out reading one long at a time in a for loop.
aioobe
@aioobe: Why don't you just implement your own version of binarySearch for a LongBuffer? Should be fairly easy, and then you are done. :-)
mreichelt