ArrayList 上的可序列化丢失一些数据
Serializable on ArrayList losing some data
我有一个 Employee 对象的 ArrayList,其中 Employee class 实现了 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);
}
如果我尝试反序列化它,我只会得到前两个对象,而不是第三个对象。谁能试试这是为什么?
edit1:如果我一次添加所有元素,一切都很好,但不是我第一次做的方式。有什么区别?
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 个元素
您的代码发生了什么:
- 你将列表写入文件,两个条目
- 您重置了流
- 你再写一遍列表,有三个个条目
因此您的文件包含两个 值,是的。两个列表,一个有 2 个,一个有 3 个条目。
换句话说:reset()
不会重置 已写入文件的内容!您编写了包含两个条目的 one 列表。你只是重置存储对象的信息,让emp1和emp2重新序列化完全。如果不调用 reset,JVM 就会明白它不需要再次完全序列化 emp1 和 emp2。
含义:默认情况下,JVM 压缩 要传输的数据量。它会记住哪些对象已经写入,而不是重复写入它们,它只会将 "object X that was serialized earlier on comes again" 之类的内容写入流中。
所以:我认为您只是没有理解reset()
方法的要点。解决方案:阅读一个小教程,例如 tutorialspoint.
中的那个
根据 OP 的最新评论进行编辑:
你要求的这种方式是不可能的。您正在编写 list 个对象。这意味着此时该列表的所有条目都将写入文件。 JVM 会记住 "that list has been written already",因此它将 不会 再次写入,即使其内部状态在此期间发生了变化。
基本上ObjectOutputStream
记住写入它的对象。如果再次写入相同的对象(通过引用),则不会对其进行序列化,而是将对先前序列化数据的引用写入流。 reset()
方法清理了 ObjectOutputStream
的内部数据结构,并允许您再次写入相同的对象。 reset()
不会丢弃已写入流的数据。
如果您尝试将流反序列化为两个 ArrayList,您将得到一个包含两个元素和一个包含三个元素的列表。
如果您删除对 reset()
方法的调用,那么您将得到两个包含两个元素的数组列表(一个实际序列化,另一个作为对前一个序列化实例的引用)
正如 GhostCat 和 uaraven 已经提到的那样,重置并不是您期望的那样,您应该查看有关序列化的教程,也许可以考虑使用某物。否则,如果这不适合您的用例。
如果创建一个新的 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);
}
}
我有一个 Employee 对象的 ArrayList,其中 Employee class 实现了 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);
}
如果我尝试反序列化它,我只会得到前两个对象,而不是第三个对象。谁能试试这是为什么?
edit1:如果我一次添加所有元素,一切都很好,但不是我第一次做的方式。有什么区别?
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 个元素
您的代码发生了什么:
- 你将列表写入文件,两个条目
- 您重置了流
- 你再写一遍列表,有三个个条目
因此您的文件包含两个 值,是的。两个列表,一个有 2 个,一个有 3 个条目。
换句话说:reset()
不会重置 已写入文件的内容!您编写了包含两个条目的 one 列表。你只是重置存储对象的信息,让emp1和emp2重新序列化完全。如果不调用 reset,JVM 就会明白它不需要再次完全序列化 emp1 和 emp2。
含义:默认情况下,JVM 压缩 要传输的数据量。它会记住哪些对象已经写入,而不是重复写入它们,它只会将 "object X that was serialized earlier on comes again" 之类的内容写入流中。
所以:我认为您只是没有理解reset()
方法的要点。解决方案:阅读一个小教程,例如 tutorialspoint.
根据 OP 的最新评论进行编辑:
你要求的这种方式是不可能的。您正在编写 list 个对象。这意味着此时该列表的所有条目都将写入文件。 JVM 会记住 "that list has been written already",因此它将 不会 再次写入,即使其内部状态在此期间发生了变化。
基本上ObjectOutputStream
记住写入它的对象。如果再次写入相同的对象(通过引用),则不会对其进行序列化,而是将对先前序列化数据的引用写入流。 reset()
方法清理了 ObjectOutputStream
的内部数据结构,并允许您再次写入相同的对象。 reset()
不会丢弃已写入流的数据。
如果您尝试将流反序列化为两个 ArrayList,您将得到一个包含两个元素和一个包含三个元素的列表。
如果您删除对 reset()
方法的调用,那么您将得到两个包含两个元素的数组列表(一个实际序列化,另一个作为对前一个序列化实例的引用)
正如 GhostCat 和 uaraven 已经提到的那样,重置并不是您期望的那样,您应该查看有关序列化的教程,也许可以考虑使用某物。否则,如果这不适合您的用例。
如果创建一个新的 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);
}
}