26

I'm learning about socket programming in Java. I've seen client/server app examples with some using DataOutputStream, and some using ObjectOutputStream.

What's the difference between the two?

Is there a performance difference?

Caner
  • 57,267
  • 35
  • 174
  • 180
magritte
  • 7,396
  • 10
  • 59
  • 79
  • Read [***ObjectOutputStream***](http://docs.oracle.com/javase/6/docs/api/java/io/ObjectInputStream.html) for a detailed, with example, explanation. – Ravinder Reddy Jul 17 '12 at 11:00

4 Answers4

33

DataInput/OutputStream performs generally better because its much simpler. It can only read/write primtive types and Strings.

ObjectInput/OutputStream can read/write any object type was well as primitives. It is less efficient but much easier to use if you want to send complex data.

I would assume that the Object*Stream is the best choice until you know that its performance is an issue.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
12

This might be useful for people still looking for answers several years later... According to my tests on a recent JVM (1.8_51), the ObjectOutput/InputStream is surprisingly almost 2x times faster than DataOutput/InputStream for reading/writing a huge array of double!

Below are the results for writing 10 million items array (for 1 million the results are the essentially the same). I also included the text format (BufferedWriter/Reader) for the sake of completeness:

TestObjectStream written 10000000 items, took: 409ms, or 24449.8778 items/ms, filesize 80390629b
TestDataStream written 10000000 items, took: 727ms, or 13755.1582 items/ms, filesize 80000000b
TestBufferedWriter written 10000000 items, took: 13700ms, or 729.9270 items/ms, filesize 224486395b

Reading:

TestObjectStream read 10000000 items, took: 250ms, or 40000.0000 items/ms, filesize 80390629b
TestDataStream read 10000000 items, took: 424ms, or 23584.9057 items/ms, filesize 80000000b
TestBufferedWriter read 10000000 items, took: 6298ms, or 1587.8057 items/ms, filesize 224486395b

I believe Oracle has heavily optimized the JVM for using ObjectStreams in last Java releases, as this is the most common way of writing/reading data (including serialization), and thus is located on the Java performance critical path.

So looks like today there's no much reason anymore to use DataStreams. "Don't try to outsmart JVM", just use the most straightforward way, which is ObjectStreams :)

Here's the code for the test:

class Generator {
    private int seed = 1235436537;
    double generate(int i) {
        seed = (seed + 1235436537) % 936855463;
        return seed / (i + 1.) / 524323.;
    }
}

class Data {
    public final double[] array;
    public Data(final double[] array) {
        this.array = array;
    }
}

class TestObjectStream {
    public void write(File dest, Data data) {
        try (ObjectOutputStream out = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(dest)))) {
            for (int i = 0; i < data.array.length; i++) {
                out.writeDouble(data.array[i]);
            }
        } catch (IOException e) {
            throw new RuntimeIoException(e);
        }
    }
    public void read(File dest, Data data) {
        try (ObjectInputStream in = new ObjectInputStream(new BufferedInputStream(new FileInputStream(dest)))) {
            for (int i = 0; i < data.array.length; i++) {
                data.array[i] = in.readDouble();
            }
        } catch (IOException e) {
            throw new RuntimeIoException(e);
        }
    }
}

class TestDataStream {
    public void write(File dest, Data data) {
        try (DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dest)))) {
            for (int i = 0; i < data.array.length; i++) {
                out.writeDouble(data.array[i]);
            }
        } catch (IOException e) {
            throw new RuntimeIoException(e);
        }
    }
    public void read(File dest, Data data) {
        try (DataInputStream in = new DataInputStream(new BufferedInputStream(new FileInputStream(dest)))) {
            for (int i = 0; i < data.array.length; i++) {
                data.array[i] = in.readDouble();
            }
        } catch (IOException e) {
            throw new RuntimeIoException(e);
        }
    }
}

class TestBufferedWriter {
    public void write(File dest, Data data) {
        try (BufferedWriter out = new BufferedWriter(new FileWriter(dest))) {
            for (int i = 0; i < data.array.length; i++) {
                out.write(Double.toString(data.array[i]));
                out.newLine();
            }
        } catch (IOException e) {
            throw new RuntimeIoException(e);
        }
    }
    public void read(File dest, Data data) {
        try (BufferedReader in = new BufferedReader(new FileReader(dest))) {
            String line = in.readLine();
            int i = 0;
            while (line != null) {
                if(!line.isEmpty()) {
                    data.array[i++] = Double.parseDouble(line);
                }
                line = in.readLine();
            }
        } catch (IOException e) {
            throw new RuntimeIoException(e);
        }
    }
}

@Test
public void testWrite() throws Exception {
    int N = 10000000;
    double[] array = new double[N];
    Generator gen = new Generator();
    for (int i = 0; i < array.length; i++) {
        array[i] = gen.generate(i);
    }
    Data data = new Data(array);

    Map<Class, BiConsumer<File, Data>> subjects = new LinkedHashMap<>();
    subjects.put(TestDataStream.class, new TestDataStream()::write);
    subjects.put(TestObjectStream.class, new TestObjectStream()::write);
    subjects.put(TestBufferedWriter.class, new TestBufferedWriter()::write);

    subjects.forEach((aClass, fileDataBiConsumer) -> {

        File f = new File("test." + aClass.getName());

        long start = System.nanoTime();
        fileDataBiConsumer.accept(f, data);
        long took = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

        System.out.println(aClass.getSimpleName() + " written " + N + " items, took: " + took + "ms, or " + String.format("%.4f", (N / (double)took)) + " items/ms, filesize " + f.length() + "b");
    });
}


@Test
public void testRead() throws Exception {
    int N = 10000000;
    double[] array = new double[N];
    Data data = new Data(array);

    Map<Class, BiConsumer<File, Data>> subjects = new LinkedHashMap<>();
    subjects.put(TestDataStream.class, new TestDataStream()::read);
    subjects.put(TestObjectStream.class, new TestObjectStream()::read);
    subjects.put(TestBufferedWriter.class, new TestBufferedWriter()::read);

    subjects.forEach((aClass, fileDataBiConsumer) -> {
        File f = new File("test." + aClass.getName());

        long start = System.nanoTime();
        fileDataBiConsumer.accept(f, data);
        long took = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

        System.out.println(aClass.getSimpleName() + " read " + N + " items, took: " + took + "ms, or " + String.format("%.4f", (N / (double)took)) + " items/ms, filesize " + f.length() + "b");
    });
}
Denis
  • 557
  • 5
  • 17
  • Interesting! Thanks for the info. – Andy Dec 17 '15 at 22:15
  • Maybe running the TestDataStream after first running TestObjectStream slowed it down. Data stream and Object stream use the same logic for reading/writing to stream. Then why would their runtime be different? – Mohit Atray Aug 28 '21 at 20:08
8

DataOutputStream and ObjectOutputStream: when handling basic types, there is no difference apart from the header that ObjectOutputStream creates.

With the ObjectOutputStream class, instances of a class that implements Serializable can be written to the output stream, and can be read back with ObjectInputStream.

DataOutputStream can only handle basic types.

user207421
  • 305,947
  • 44
  • 307
  • 483
Nikhil
  • 16,194
  • 20
  • 64
  • 81
0

Only objects that implement the java.io.Serializable interface can be written to streams using ObjectOutputStream.Primitive data types can also be written to the stream using the appropriate methods from DataOutput. Strings can also be written using the writeUTF method. But DataInputStream on the other hand lets an application write primitive Java data types to an output stream in a portable way.

Object OutputStream

Data Input Stream

sasidhar
  • 7,523
  • 15
  • 49
  • 75
  • Incorrect. Primitives can be written to an `ObjectOutputStream`. – user207421 Jul 18 '12 at 04:11
  • `Only objects that support the java.io.Serializable interface can be written to streams` please see this line from the above javadocs link i provided. The class description says it can be written when the Serializable interface is implemented.I never mentioned primitive types can't be written to this stream – sasidhar Jul 18 '12 at 04:34
  • @sasidhar Then you should consider changing the wording of your answer, because it implies that only `Objects` can be written to the `ObjectOutputStream`. (Which is probably why you have 2 downvotes) – arkon Jun 11 '13 at 17:50