Сериализуемый в ArrayList, теряющий часть данных

#java #serializable

#java #сериализуемый

Вопрос:

У меня есть ArrayList объектов Employee, где класс Employee реализует Serializable. Я использую этот код для записи списков в файл:

 ArrayList<Employee> empList = new ArrayList<>();
  FileOutputStream fos = new FileOutputStream("EmpObject.ser");
  ObjectOutputStream oos = new ObjectOutputStream(fos);
  // write object to file
  empList .add(emp1);
  empList .add(emp2);
  oos.writeObject(empList);

  empList .add(emp3);

  oos.writeObject(empList);
}
  

Если я попытаюсь десериализовать его, я получу только первые два объекта, а не третий. Кто-нибудь, пожалуйста, может попробовать, почему это?

правка 1: Если я добавлю все элементы сразу, все будет хорошо, но не так, как я сделал сначала. В чем разница?

 ArrayList<Employee> empList = new ArrayList<>();
  FileOutputStream fos = new FileOutputStream("EmpObject.ser");
  ObjectOutputStream oos = new ObjectOutputStream(fos);
  // write object to file
  empList .add(emp1);
  empList .add(emp2);
  empList .add(emp3);
  oos.writeObject(empList);
}
  

После этого у меня есть 3 элемента

Ответ №1:

Поскольку GhostCat и uaraven уже упоминали, что сброс не соответствует вашим ожиданиям, и вам следует взглянуть на руководство по сериализации и, возможно, рассмотреть возможность использования sth. еще, если это не соответствует вашему варианту использования.

Ваш код мог бы выглядеть следующим образом при создании нового FileOutputStream:

 import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class SerializationTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        String path = "EmpObject.ser";

        ArrayList<Employee> empList = new ArrayList<>();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));

        empList.add(emp1);
        empList.add(emp2);
        oos.writeObject(empList);

        empList.add(emp3);
        // Create a new FileOutputStream to override the files content instead of appending the new employee list
        oos = new ObjectOutputStream( new FileOutputStream(path));
        oos.writeObject(empList);

        ObjectInputStream objectinputstream = new ObjectInputStream(new FileInputStream(path));
        List<Employee> readCase = (List<Employee>) objectinputstream.readObject();

        System.out.println(readCase);
    }
}
  

Комментарии:

1. Большое вам всем спасибо. Прошел через руководство. Теперь понял. Просто нужно было создать новый выходной поток, как упоминалось @FlorianDe. Большое вам спасибо за помощь

Ответ №2:

Что происходит с вашим кодом:

  • вы записываете список в файл с двумя записями
  • вы сбрасываете поток
  • вы снова записываете список, содержащий три записи

Таким образом, ваш файл содержит два значения, да. Два списка, один с 2, другой с 3 записями.

Другими словами: reset() не сбрасывает то, что было записано в файл! Вы написали один список с двумя записями. Вы всего лишь сбрасываете информацию о сохраненных объектах, так что emp1 и emp2 снова сериализуются полностью. Без вызова reset JVM поняла бы, что ей не нужно снова полностью сериализовать emp1 и emp2.

Значение: по умолчанию JVM сжимает объем данных для передачи. Он запоминает, какие объекты уже были записаны, и вместо того, чтобы записывать их повторно, он только записывает что-то вроде «объект X, который был сериализован ранее, возвращается снова» в поток.

Итак: я думаю, вы просто не понимаете суть reset() метода. Решение: прочитайте небольшой учебник, подобный тому, что был в tutorialspoint.

Редактировать с учетом последнего комментария OP:

То, о чем вы просите, невозможно таким образом. Вы пишете объекты списка. Это означает, что все записи этого списка в этот момент записываются в файл. JVM запоминает «этот список уже был записан», поэтому она не будет записывать его снова, даже если его внутреннее состояние за это время изменилось.

Ответ №3:

В основном ObjectOutputStream запоминает объекты, которые в него записаны. Если тот же объект (по ссылке) записывается снова, он не сериализуется, а скорее ссылка на предыдущие сериализованные данные записывается в stream. reset() метод очищает внутренние структуры данных ObjectOutputStream и позволяет вам снова записать тот же объект. reset() не удаляет данные, уже записанные в поток.

Если вы попытаетесь десериализовать свой поток в два ArrayLists, вы получите один с двумя элементами и один с тремя элементами.

Если вы удалите вызов reset() метода, то вы получите два списка массивов с двумя элементами (один фактически сериализованный, а другой как ссылка на предыдущий сериализованный экземпляр)

Комментарии:

1. OP вызывает reset в своем первом примере, и он говорит, что это все еще не работает?

2. На самом деле я не хочу reset() . Я просто хочу один ArrayList с 3 элементами, НО я не хочу добавлять все элементы в список сразу. Сначала 2 элемента, а затем еще один.

3. Если вы хотите написать ArrayList один раз, то напишите его один раз. Вы не можете записать список массива с двумя элементами в файл, а затем ожидать чтения из него трех элементов. Вы можете создать новый FileOutputStream и перезаписать уже записанные в него данные, но вы не можете добавить новые данные в тот же поток и ожидать, что ранее записанные данные каким-то образом исчезнут.

4. о, так что это невозможно сделать таким образом, тогда каков наилучший способ записать, а затем десериализовать его

5. Большое вам спасибо. Я понимаю, что то, что я пытался сделать, было невозможно. Спасибо