2

I have a block of code, that deserializes multiple objects from file. How can i avoid using a while(true)?

ObjectInputStream in = new ObjectInputStream(new FileInputStream(
        filename));

while (true) {
    try {
        MyObject o = (MyObject) in.readObject();
        // Do something with the object
    } catch (EOFException e) {
        break;
    }
}

in.close();
10
  • 2
    Why are you trying to avoid the while(true)? How are you writing it into the file? Commented Aug 18, 2014 at 10:43
  • maybe use a collection while serializing? Commented Aug 18, 2014 at 10:44
  • Also wow, didn't realize anyone is still using Java's serialization and not something sane like serializing to JSON/XML or something so you can read it in a text editor or with protocol buffers if your aim is performance. Commented Aug 18, 2014 at 10:48
  • JSON is only popular because Web/REST Services tends to use it instead of XML, plain text or whatever. Reading the data is really useful when you need to debug, but what you need is the data but you don't really care about a particular format. Commented Aug 18, 2014 at 11:13
  • @BenjaminGruenbaum Irrelevant. Unless the deserializing API restricts the bandwidth by reserving null or some other value as an end of stream marker, an exception must be thrown. An out of band marker is required. Commented Aug 18, 2014 at 11:47

3 Answers 3

1

You should write either a collection (with a size), or a put a marker before each object:

try {
  for (;in.readBoolean();) {
    MyObject o = (MyObject) in.readObject();

  }
} catch (EOFException e) {
  // ...
}

When you write your object, write a boolean just before (it will however take 1 byte if I do remember well that part):

for (MyObject o : iterable) {
  out.writeBoolean(true);
  out.writeObject(o);
}
out.writeBoolean(false);

If iterable is a collection or map, you can use default serialization:

out.writeObject(iterable); // default collection serialization

Beside, don't catch an exception for each item, catch it globally (especially EOFException!): it is better for performance reasons.

I don't know if you work with Java 7, but your code + my for loop can be written like this:

try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(     filename))) {
  for (;in.readBoolean();) {
    MyObject o = (MyObject) in.readObject();

  }
} catch (EOFException e) {
  // ...
}
// no need to close, the try-with-resources do the job for you.
Sign up to request clarification or add additional context in comments.

2 Comments

What if you don't know the number of objects in advance? What if you change your mind afterwards? What if the program exits prematurely?
That's the purpose of the readBoolean, which expect the writer to know when it is finished to tell the reader there is nothing left to read. Beside, he is reading a file, which you can expect to have an end.
1

How can i avoid using a while(true)?

You can't.

More to the point, why do you think you want to?

This is a classic example of the tail wagging the dog. EOFException is thrown to indicate end of stream. Ergo you have to catch it, and ergo you have to loop until it is thrown, ergo you have to use while (true) or one of its cognates.

The exception thought police would have you prepend an object count, taking the curious position that external data structures should be designed to suit the coder's phobias, and overlooking that you may not know it in advance, or may need to change your mind, or may need to exit prematurely; or would have you write a null as an end-of-stream marker, overlooking that it prevents the use of null for any other purpose; and in both cases overlooking the fact that the API is already designed to throw EOFException, and already works the way it already works, so you already have to code accordingly.

5 Comments

+1 for "exception thought police", although I do consider myself part of said crowd.
@ErikMadsen it's not a matter of "thought police", it's a matter of "use exceptions for exceptional conditions" - this answer presents it as if there are two alternatives: Be an idiot and don't handle exceptions at all or use exceptions for flow control where in fact there are solutions other than that. What if the OP is serializing into a file called "Top 5 MyObjects" and they are always reading 5 items? What if they are serializing an ArrayList and they don't really care about space complexity that much? What if serialization isn't even the correct approach here?
Not to mention the olden' horror stories of serializing/deserializing from/to the wrong type that get caused by not knowing your page's schema. All to avoid the extra byte that a marker is when serialization is probably far from the most efficient or even the correct approach anyway.
@BenjaminGruenbaum I am answering the question he asked. You seem to be commenting on something else entirely. The notion that end of file isn't exceptional is foreign to me, and more importantly to the designers of readObject(), and the general attitude it embodies seems to rely on just defining away 'exceptional condition' so that nothing whatever is included in it. If you personally don't like Serialization, don't use it, but when discussing a question about it let's answer it.
Your answer sounds like if you don't read it exactly like OP does you can't handle an exceptional case when in fact that's not the case at all. You can read it without a for loop (for example, if you serialize it at a container) and still handle exceptions just fine. Moreover, you make it seem like this way is "The way the Java language developers intended!" and is canonical where in fact it is also not the case. I disagree with both those statements. However, I can't argue about a better/worse solution for OP's problem because they have not clarified it or answered the comments.
0

The code that I'm proposing let you to serialize and deserialize multiple objects really easily without having any problems and avoiding the awful (in my opinion) while true:

public class EntityClass implements Serializable{
private int intVal;
private String stringVal;

public EntityClass(int intVal, String stringVal) {
    this.intVal = intVal;
    this.stringVal = stringVal;
}

@Override
public String toString() {
    return "EntityClass{" +
            "intVal=" + intVal +
            ", stringVal='" + stringVal + '\'' +
            '}';
}

public static void main(String[] args) throws IOException, ClassNotFoundException {
    EntityClass a = new EntityClass(1, "1");
    EntityClass b = new EntityClass(2, "2");
    EntityClass c = new EntityClass(3, "3");

    ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream("out"));

    stream.writeObject(a);
    stream.writeObject(b);
    stream.writeObject(c);

    stream.close();

    ObjectInputStream streamRead = new ObjectInputStream(new FileInputStream("out"));

    EntityClass[] entities = new EntityClass[3];
    int cont  = 0;

    try {
        while (streamRead.available() >= 0) {
            entities[cont] = (EntityClass) streamRead.readObject();
            System.out.println(entities[cont]);
            cont++;
        }
    } catch (EOFException exp) {

    } finally {
        streamRead.close();
    }
}

}

1 Comment

InputStream.available() is not a valid test for end of stream. See the Javadoc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.