为什么ArrayList使用瞬态存储?

41

我在阅读Java的ArrayList源码时遇到了它的支撑数组声明:

private transient Object[] elementData;

为什么这个需要是瞬态的?为什么这个类不能被序列化?

感谢帮助!

6个回答

43

可以被序列化; ArrayList 类会自己处理,而不是使用默认的机制。查看该类中的 writeObject()readObject() 方法,它们是标准序列化机制的一部分。

如果你查看源码,你会发现writeObject()方法不会保存支持数组。相反,它将元素(包括 null 值)一个接一个地序列化到size()限制为止。这避免了序列化数组及特别是数组末尾的任何未使用插槽的开销。在反序列化时,readObject() 会创建一个新的支持数组,大小为所需最小值。


9
为什么这需要是瞬态的?
这是因为提供了自定义的readObject和writeObject方法,比默认机制更好地完成序列化。具体而言,writeObject方法只写入元素的大小和顺序。这避免了私有数组对象1)具有自己的标题和开销,2)通常带有null填充的序列化。节省的空间可以相当可观。
为什么这个类不能被序列化?
整个ArrayList类都可以进行序列化[1]。 Object []可以直接序列化,但他们选择标记它作为瞬态来实现另一种方式的序列化。
1 - 实际上,这取决于元素的运行时类型。例如,如果尝试序列化包含Thread引用的ArrayList,则会对第一个非空引用抛出运行时异常。

只是想确认一下 - "在这种情况下,字段根本不需要严格声明为瞬态(transient)", 因为提供了自定义的 readObjectwriteObject 方法? - Jianxin Gao
那个旁白是不正确的,我已经将其删除。实际上,readObject / writeObject 使用 defaultReadObject / writeObject 方法来处理一些字段。因此,其他字段需要标记为“transient”。 - Stephen C

6

ArrayList实现了Serializable接口,因此它可以被序列化。这正是为什么私有的后备数组是transient的原因,因为它不会随着类中的其他数据一起被序列化,所有这些都由ArrayListwriteObjectreadObject方法处理。


2
因为它实现了显式的序列化。请参见ArrayList#writeObject。

1

在Stephen C的回答基础上进行扩展,我想更正他关于在ArrayLists中使用transient来提高可读性的说明。这可能更适合作为他回答下面的评论,但我还没有那个能力!

虽然将字段标记为transient有助于提高可读性,但由于自定义的readObjectwriteObject方法调用了java.io.ObjectInputStreamdefaultReadObjectjava.io.ObjectOutputStreamdefaultWriteObject方法,因此这也是必要的。这些方法将处理所有未标记为transient的字段(例如size)的序列化工作。

有关更多详细信息,请参见ObjectOutputStream的源代码:https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/io/ObjectOutputStream.java#L431


是的...我刚刚也注意到了。我已经更正了我的答案。谢谢。 - Stephen C

0

该变量不可序列化。

  • 如果该变量不可序列化,则在尝试对其进行序列化时,序列化机制将抛出异常。为避免此情况,您可以将变量声明为瞬态的。

该变量是多余的。

  • 假设实例缓存了计算结果。在本地,我们可能希望存储计算结果,以节省一些处理器时间。但是当我们将对象通过网络发送时,我们可能更担心消耗带宽,因此会丢弃缓存的计算结果,因为我们随时可以重新生成它。

链接:http://onjava.com/pub/a/onjava/excerpt/JavaRMI_10/index.html?page=3


这些并没有解释为什么在这种情况下elementData被标记为transient。1)Object[]元素将是可序列化的,否则ArrayList的序列化将失败。2)ArrayList的元素不是“冗余”的。请参见其他答案以获取正确的解释。 - Stephen C

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接