序列化和反序列化键作为字符串的映射

2022-09-02 12:13:02

我打算序列化和反序列化一个哈希映射,其键是一个字符串。

从Josh Bloch的《Effective Java》中,我理解了以下内容。第222页

例如,考虑哈希表的情况。物理表示形式是包含键值条目的哈希存储桶序列。条目所在的存储桶是键的哈希代码的函数,通常不保证从 JVM 实现到 JVM 实现是相同的。事实上,它甚至不能保证在相同的JVM实现上运行到运行都是相同的。因此,接受哈希表的默认序列化形式将构成一个严重的错误。序列化和反序列化哈希表可能会生成一个对象,其不变量已严重损坏。

我的问题是:1)一般来说,覆盖地图键类的等于和哈希码可以解决这个问题,并且可以正确恢复地图吗?

2)如果我的键是一个字符串,并且String类已经覆盖了hashCode()方法,我是否仍然会遇到上面描述的问题。(我看到一个错误,让我认为这可能仍然是一个问题,即使密钥是带有覆盖哈希码的字符串。

3)以前,我通过序列化条目数组(键,值)来解决这个问题,并且在反序列化时,我会重建映射。我想知道是否有更好的方法。

4)如果问题1和2的答案仍然不能保证,有人可以解释为什么吗?如果哈希码是相同的,它们会跨JVM转到相同的存储桶吗?

谢谢,格蕾丝


答案 1

的序列化形式不会序列化存储桶本身,并且哈希代码不是持久状态的一部分。来自 javadocs:java.util.HashMap

串行数据:HashMap 的容量(存储桶数组的长度)发出 (int),后跟 HashMap 的大小(键值映射的数量),然后是 HashMap 表示的每个键值映射的键 (对象) 和值 (对象)。entrySet().iterator()

http://java.sun.com/j2se/1.5.0/docs/api/serialized-form.html#java.util.HashMap 相比

持久状态基本上包括键和值以及一些内务管理。当反序列化时,哈希映射被完全重建;重新哈希键并将其放置在适当的存储桶中。

因此,添加字符串键应该可以正常工作。我猜你的错误在别处。

编辑:这是一个 junit 4 测试用例,它序列化和反序列化映射,并最小化更改哈希码的 VM。测试通过,尽管哈希码在反序列化后有所不同。

import org.junit.Assert;
import org.junit.Test;

import java.io.*;
import java.util.HashMap;

public class HashMapTest
{
    @Test
    public void testHashMapSerialization() throws IOException, ClassNotFoundException
    {
        HashMap map = new HashMap();
        map.put(new Key("abc"), 1);
        map.put(new Key("def"), 2);

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream objOut = new ObjectOutputStream(out);
        objOut.writeObject(map);
        objOut.close();
        Key.xor = 0x7555AAAA; // make the hashcodes different
        ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(out.toByteArray()));
        HashMap actual = (HashMap) objIn.readObject();
        // now try to get a value
        Assert.assertEquals(2, actual.get(new Key("def")));
    }

    static class Key implements Serializable
    {
        private String  keyString;
        static int xor = 0;

        Key(String keyString)
        {
            this.keyString = keyString;
        }

        @Override
        public int hashCode()
        {
            return keyString.hashCode()^xor;
        }

        @Override
        public boolean equals(Object obj)
        {
            Key otherKey = (Key) obj;
            return keyString.equals(otherKey.keyString);
        }
    }

}

答案 2

我99%确定HashMap和HashSet的JVM实现可以解决这个问题。它们具有自定义序列化和反序列化处理程序。我现在面前没有布洛赫的书,但我相信他是在解释这个挑战,而不是说你不能在实践中可靠地序列化java.util.HashMap。