为什么 TreeSet 中的底层数据结构被标记为 transient?

16 浏览
0 Comments

为什么 TreeSet 中的底层数据结构被标记为 transient?

我在查看 HashMap 的源代码。 \n一个 HashMap 实现了 Serializable 接口。 \n好的,这样可以将其作为对象进行持久化/传输。 \n但是我发现哈希表本身被标记为 transient。 \n我不明白这点。如果将其标记为 transient,难道不意味着它不会被序列化吗? \n但是所有的数据都在表中。那为什么要设置为 transient? \n也许我对 Serializable 的工作原理有些困惑?

0
0 Comments

为什么TreeSet中底层的数据结构被标记为transient?

transient关键字表示一个字段不应该被包含在类的序列化表示中。HashMap的Entry[]表只是一个加速结构,它允许快速查找存储的条目。整个表本身不需要被序列化,只需要序列化它包含的条目,因为在从条目列表反序列化时可以重新构建表。

这是为了节省空间还是为了提高速度?因为在反序列化时,你最终会得到一个相同大小的表。

是为了节省空间。Entry表比键的列表更大。

0
0 Comments

为了回答问题"Why the underlying Data structure in TreeSet is marked transient",我们首先需要了解为什么在HashMap中使用了自定义序列化方法,以及为什么在序列化过程中不包含底层数据结构。

在HashMap中,为了实现自定义的序列化,它使用了writeObject和readObject方法,而不是简单地让其字段正常序列化。它将桶的数量、总大小以及每个entry写入流中,并在反序列化时根据这些字段重建自身。正如tzaman所说,序列化形式中的table本身是不必要的,因此它不会被序列化以节省空间。

为了了解更多关于这些方法以及其他自定义序列化方法(如writeReplace和readResolve)的信息,可以在Serializable javadoc中阅读更多内容。

根据上述信息,我们可以解释为什么在TreeSet的底层数据结构中使用了transient关键字。对象的哈希码可能会在程序的不同运行之间发生变化,例如,如果hashCode()使用了默认的Object实现。因此,在反序列化过程中,哈希表必须被重建。

解决方法:

为了解决在反序列化过程中底层数据结构的重建问题,可以通过实现自定义的writeObject和readObject方法来进行处理。在writeObject方法中,可以将需要序列化的字段写入流中,而在readObject方法中,可以从流中读取这些字段并重建底层数据结构。

下面是一个示例代码,展示了如何在TreeSet中实现自定义序列化方法:

public class MyTreeSet implements Serializable {
    private transient TreeSet set;
    public MyTreeSet() {
        set = new TreeSet<>();
    }
    private void writeObject(ObjectOutputStream out) throws IOException {
        // 序列化需要的字段
        out.defaultWriteObject();
    }
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        // 从流中读取字段并重建底层数据结构
        in.defaultReadObject();
        set = new TreeSet<>();
    }
}

在上面的示例中,set字段被标记为transient,以便在序列化过程中不包含底层数据结构。然后,通过实现writeObject和readObject方法,可以在序列化和反序列化过程中进行必要的处理。

总结起来,为了节省空间和处理对象哈希码变化的问题,TreeSet的底层数据结构被标记为transient,在序列化过程中不包含它们。为了解决底层数据结构的重建问题,可以实现自定义的writeObject和readObject方法进行处理。

0