Я изучаю программирование сокетов в Java. Я видел примеры приложений клиент/сервер с некоторыми, используя DataOutputStream
, а некоторые - с помощью ObjectOutputStream
.
Какая разница между этими двумя?
Есть ли разница в производительности?
Я изучаю программирование сокетов в Java. Я видел примеры приложений клиент/сервер с некоторыми, используя DataOutputStream
, а некоторые - с помощью ObjectOutputStream
.
Какая разница между этими двумя?
Есть ли разница в производительности?
DataInput/OutputStream работает лучше, потому что его намного проще. Он может только читать/писать примитивные типы и строки.
ObjectInput/OutputStream может читать/записывать любой тип объекта, а также примитивы. Он менее эффективен, но гораздо проще в использовании, если вы хотите отправлять сложные данные.
Я бы предположил, что Object * Stream - лучший выбор, пока вы не узнаете, что его производительность является проблемой.
DataOutputStream
и ObjectOutputStream
: при работе с базовыми типами нет разницы, кроме заголовка, создаваемого ObjectOutputStream
.
С классом ObjectOutputStream
экземпляры класса, реализующего Serializable
, могут быть записаны в выходной поток и могут быть считаны с помощью ObjectInputStream
.
DataOutputStream
может обрабатывать только базовые типы.
Это может быть полезно для людей, которые еще ищут ответы несколько лет спустя... Согласно моим тестам на недавнем JVM (1.8_51), ObjectOutput/InputStream
на удивление почти в 2 раза быстрее, чем DataOutput/InputStream
для чтения/записи огромный массив двойных!
Ниже приведены результаты для записи 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.
Итак, похоже, что сегодня больше нет смысла использовать DataStream
s. "Не пытайтесь перехитрить 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");
});
}
Только те объекты, которые реализуют интерфейс java.io.Serializable
, могут быть записаны в потоки с использованием ObjectOutputStream
. В потоке могут быть записаны также допустимые типы данных, используя соответствующие методы из DataOutput. Строки также могут быть записаны с использованием метода writeUTF. Но DataInputStream
, с другой стороны, позволяет приложению переносить примитивные типы данных Java в выходной поток переносимым образом.