В чем разница между DataOutputStream и ObjectOutputStream?

Я учусь программированию сокетов в Java. Я видел примеры приложений клиент / сервер с некоторыми использованием DataOutputStreamи некоторые используют ObjectOutputStream,

Какая разница между этими двумя?

Есть ли разница в производительности?

4 ответа

Решение

DataInput/OutputStream в целом работает лучше, потому что он намного проще. Он может только читать / записывать примитивные типы и строки.

ObjectInput / OutputStream может читать / записывать объекты любого типа, а также примитивы. Это менее эффективно, но гораздо проще в использовании, если вы хотите отправить сложные данные.

Я бы предположил, что Object*Stream - лучший выбор, пока вы не знаете, что его производительность является проблемой.

Это может быть полезно для людей, которые все еще ищут ответы несколько лет спустя... Согласно моим тестам на недавней JVM (1.8_51), ObjectOutput/InputStream на удивление почти в 2 раза быстрее, чем DataOutput/InputStream для чтения / записи огромного массива double!

Ниже приведены результаты для записи массива 10 миллионов элементов (для 1 миллиона результаты практически одинаковы). Я также включил текстовый формат (BufferedWriter/Reader) для полноты картины:

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

Чтение:

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

Я считаю, что Oracle сильно оптимизировал JVM для использования ObjectStreamв последних выпусках Java, так как это наиболее распространенный способ записи / чтения данных (включая сериализацию), и, следовательно, он находится на критическом пути производительности Java

Похоже, что сегодня больше нет причин использовать DataStreams. "Не пытайтесь перехитрить JVM", просто используйте самый простой способ, который ObjectStreamс:)

Вот код для теста:

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");
    });
}

DataOutputStream а также ObjectOutputStream: при обработке основных типов нет разницы, кроме заголовка, который ObjectOutputStream создает.

С ObjectOutputStream класс, экземпляры класса, который реализует Serializable может быть записан в выходной поток, и может быть прочитан обратно с ObjectInputStream,

DataOutputStream может обрабатывать только основные типы.

Только объекты, которые реализуют java.io.Serializable интерфейс может быть записан в потоки с помощью ObjectOutputStreamПримитивные типы данных также могут быть записаны в поток с использованием соответствующих методов из DataOutput. Строки также могут быть записаны с использованием метода writeUTF. Но DataInputStream с другой стороны, позволяет приложению записывать примитивные типы данных Java в выходной поток переносимым способом.

Объект OutputStream

Поток ввода данных

Другие вопросы по тегам