Reading Objects from Random Access File

AnkurVj picture AnkurVj · Sep 12, 2011 · Viewed 10.2k times · Source

I wrote a file using Java's FileChannel class that uses RandomAccessFiles. I wrote objects at various locations in the file. The objects were of variable sizes but all of the same class. I wrote the objects using the following idea :

ByteArrayOutputStream bos= new ByteArrayOutputStream();
        ObjectOutput out = new ObjectOutputStream(bos);
        out.writeObject(r);
        byte[] recordBytes= bos.toByteArray();

    ByteBuffer rbb= ByteBuffer.wrap(recordBytes);

    while(rbb.hasRemaining()) {
        fileChannel.write(rbb);
    }

Now I want to read from such a file. I dont want to have to specify the number of bytes to read. I want to be able to read the object directly using Object Input Stream. How to achieve this ?

I have to use Random Access Files because I need to write to different positions in file. I am also recording in a separate data structure, the locations where objects have been written.

Answer

parsifal picture parsifal · Sep 14, 2011

I have to use Random Access Files because I need to write to different positions in file.

No, you don't. You can reposition a FileOutputStream or FileInputStream via its channel.

That would significantly simplify your writing code as well: you wouldn't need to use the buffer or channel, and depending on your needs you could omit the ByteArrayOutputStream as well. However, as you note in a comment, you won't know the size of the object in advance, and the ByteArrayOutputStream is a useful way to verify that you don't overrun your allotted space.

Object obj = // something

FileOutputStream fos = // an initialized stream

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(obj);
oos.flush();

if (bos.size() > MAX_ALLOWED_SIZE)
   throw // or log, or whatever you want to do
else
{
    fos.getChannel().position(writeLocation);
    bos.writeTo(fos);
}

To read the objects, do the following:

FileInputStream fis = // an initialized stream

fis.getChannel().position(offsetOfSerializedObject);
ObjectInputStream iis = new ObjectInputStream(new BufferedInputStream(fis));
Object obj = iis.readObject();

One comment here: I wrapped the FileInputStream in a BufferedInputStream. In this specific case, where the file stream is repositioned before each use, that can provide a performance benefit. Be aware, however, that the buffered stream can read more bytes than are needed, and there are some situations using construct-as-needed object streams where it would be a really bad idea.