English 中文(简体)
用Java从文件中完全读取对象流的最佳方式是什么?
原标题:What is the best way to fully read a stream of objects from a file in Java?

我正在创建一个可能很长的对象日志,不想在写入文件之前将它们全部保存在内存中,所以我无法将对象的序列化集合写入文件。我正试图找出在日志记录完成后读取整个对象流的最佳方式。

我注意到以下内容不起作用:

FileInputStream fis = new FileInputStream(log);
ObjectInputStream in = new ObjectInputStream(fis);
while ((obj = in.readObject()) != null) {
  // do stuff with obj
}

因为流在到达文件末尾时抛出异常,而不是返回null(可能是因为可以向对象流写入/读取null,导致上面的循环无法正常运行)。

有没有比以下更好的方法来完成我想用上面的循环完成的事情:

FileInputStream fis = new FileInputStream(log);
ObjectInputStream in = new ObjectInputStream(fis);
try {
  while (true) {
    obj = in.readObject();
    // do stuff with obj
  }
} catch (EOFException e) {
}

这似乎有点笨拙。对于文件结束对象解决方案,以下是最佳方法吗?

private static final class EOFObject implements Serializable {
  private static final long serialVersionUID = 1L;
}

void foo() {
  Object obj;
  while (!((obj = in.readObject()) instanceof EOFObject)) {
    BidRequest bidRequest = ((BidRequestWrapper) obj).getBidRequest();
    bidRequestList.add(bidRequest);
  }
}
最佳回答

你的解决方案似乎不错。只需确保您有一个finally子句,在那里您可以关闭流。

或者,您可以创建自己的EOF对象,并在末尾添加它。因此,您可以检查当前读取的对象是否为EofObject,并且此时break

问题回答

我正在创建一个可能很长的对象日志,不想在写入文件之前将它们全部保存在内存中,所以我无法将对象的序列化集合写入文件

使用Java序列化时无法满足这一要求,因为序列化流维护对以前编写的对象的强引用,可能是为了在需要再次序列化这些对象时写回引用。这可以通过运行以下程序进行验证:

public static void main(String[] args) throws Exception {
    OutputStream os = new FileOutputStream("C:\test");
    ObjectOutputStream oos = new ObjectOutputStream(os);
    for (Integer i = 0; i < 1E9; i++) {
        oos.writeObject(i);
    }
    oos.close();
}

反序列化文件时也存在类似的问题。为了解析反向引用,流很可能会保持所有以前读取的对象处于活动状态,以解析序列化流中对这些对象的潜在反向引用。

如果您真的需要能够在流被完全写入之前释放这些对象,您可能希望为每个(批)对象使用一个新的ObjectOutputStreamObjectOutputStream.reset()-当然会失去从早期流中解析回引用的能力。也就是说,以下程序不会抛出OutOfMemoryError:

public static void main(String[] args) throws Exception {
    OutputStream os = new FileOutputStream("C:\test");
    ObjectOutputStream oos = new ObjectOutputStream(os);
    for (Integer i = 0; i < 1E9; i++) {
        oos.writeObject(i);
        oos.reset();
    }
    oos.close();
}

请注意,关于正在序列化的类的元数据将在每次重置后重新写入,这是非常浪费的(上面的程序每个Integer写入大约80个字节…),所以您不应该太频繁地重置,也许每100个对象重置一次?

对于检测流的结束,我发现bozho关于EOF对象的建议是最好的。

在每个对象后面写一个布尔值,“最后一个”对象后面跟着一个false。所以,在你写的流中:

true
<object>
true
<object>
true
<object>
false

然后,当重新读取它们时,您检查标志(您知道每个对象后面总是有一个),以决定是否读取另一个。

<code>boolean</code>将非常紧凑地存储在序列化流中,因此不应该增加太多文件大小。

您的代码不正确。readObject()在EOS处不返回null,而是抛出EOFException。所以抓住它。如果写入一个Null,则返回Null。您不需要上面建议的所有布尔值或标记对象。





相关问题
Spring Properties File

Hi have this j2ee web application developed using spring framework. I have a problem with rendering mnessages in nihongo characters from the properties file. I tried converting the file to ascii using ...

Logging a global ID in multiple components

I have a system which contains multiple applications connected together using JMS and Spring Integration. Messages get sent along a chain of applications. [App A] -> [App B] -> [App C] We set a ...

Java Library Size

If I m given two Java Libraries in Jar format, 1 having no bells and whistles, and the other having lots of them that will mostly go unused.... my question is: How will the larger, mostly unused ...

How to get the Array Class for a given Class in Java?

I have a Class variable that holds a certain type and I need to get a variable that holds the corresponding array class. The best I could come up with is this: Class arrayOfFooClass = java.lang....

SQLite , Derby vs file system

I m working on a Java desktop application that reads and writes from/to different files. I think a better solution would be to replace the file system by a SQLite database. How hard is it to migrate ...

热门标签