java HashMap 序列化和反序列化变化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5993752/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 13:52:18  来源:igfitidea点击:

HashMap serialization and deserialization changes

javaserializationhashmap

提问by dgaviola

We are working with an in memory data grid (IMDG) and we have a migration tool. In order to verify that all the objects are migrated successfully, we calculate the chucksum of the objects from its serialized version.

我们正在使用内存数据网格 (IMDG) 并且我们有一个迁移工具。为了验证所有对象迁移成功,我们从对象的序列化版本计算出对象的chucksum。

We are seeing some problems with HashMap, where we serialize it, but when we deserialize it the checksum changes. Here is a simple test case:

我们发现 HashMap 存在一些问题,我们对其进行序列化,但是当我们反序列化它时,校验和会发生变化。下面是一个简单的测试用例:

@Test
public void testMapSerialization() throws IOException, ClassNotFoundException {
    TestClass tc1 = new TestClass();
    tc1.init();
    String checksum1 = SpaceObjectUtils.calculateChecksum(tc1);

    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutput out = null;
    byte[] objBytes = null;
    out = new ObjectOutputStream(bos);
    out.writeObject(tc1);
    objBytes = bos.toByteArray();
    out.close();
    ByteArrayInputStream bis = new ByteArrayInputStream(objBytes);
    ObjectInputStream in = new ObjectInputStream(bis);
    TestClass tc2 = (TestClass) in.readObject();
    String checksum2 = SpaceObjectUtils.calculateChecksum(tc2);

    assertEquals(checksum1, checksum2);
}

The TestClass looks like this:

TestClass 看起来像这样:

class TestClass implements Serializable {
    private static final long serialVersionUID = 5528034467300853270L;

    private Map<String, Object> map;

    public TestClass() {
    }

    public Map<String, Object> getMap() {
        return map;
    }

    public void setMap(Map<String, Object> map) {
        this.map = map;
    }

    public void init() {
        map = new HashMap<String, Object>();
        map.put("name", Integer.valueOf(4));
        map.put("type", Integer.valueOf(4));
        map.put("emails", new BigDecimal("43.3"));
        map.put("theme", "sdfsd");
        map.put("notes", Integer.valueOf(4));
        map.put("addresses", Integer.valueOf(4));
        map.put("additionalInformation", new BigDecimal("43.3"));
        map.put("accessKey", "sdfsd");
        map.put("accountId", Integer.valueOf(4));
        map.put("password", Integer.valueOf(4));
        map.put("domain", new BigDecimal("43.3"));
    }
}

And this is the method to calculate the checksum:

这是计算校验和的方法:

public static String calculateChecksum(Serializable obj) {
    if (obj == null) {
        throw new IllegalArgumentException("The object cannot be null");
    }
    MessageDigest digest = null;
    try {
        digest = MessageDigest.getInstance("MD5");
    } catch (java.security.NoSuchAlgorithmException nsae) {
        throw new IllegalStateException("Algorithm MD5 is not present", nsae);
    }
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutput out = null;
    byte[] objBytes = null;
    try {
        out = new ObjectOutputStream(bos);
        out.writeObject(obj);
        objBytes = bos.toByteArray();
        out.close();
    } catch (IOException e) {
        throw new IllegalStateException(
                "There was a problem trying to get the byte stream of this object: " + obj.toString());
    }
    digest.update(objBytes);
    byte[] hash = digest.digest();
    StringBuilder hexString = new StringBuilder();
    for (int i = 0; i < hash.length; i++) {
        String hex = Integer.toHexString(0xFF & hash[i]);
        if (hex.length() == 1) {
            hexString.append('0');
        }
        hexString.append(hex);
    }
    return hexString.toString();
}

If you print the maps of tc1 and tc2, you can see that the elements are not in the same place:

如果打印tc1和tc2的maps,可以看到元素不在同一个地方:

{accessKey=sdfsd, accountId=4, theme=sdfsd, name=4, domain=43.3, additionalInformation=43.3, emails=43.3, addresses=4, notes=4, type=4, password=4}
{accessKey=sdfsd, accountId=4, name=4, theme=sdfsd, domain=43.3, emails=43.3, additionalInformation=43.3, type=4, notes=4, addresses=4, password=4}

I would like to be able to serialize the HashMap and get the same checksum when I deserialize it. Do you know if there is a solution or if I'm doing something wrong?

我希望能够序列化 HashMap 并在反序列化时获得相同的校验和。你知道是否有解决方案或者我做错了什么吗?

Thanks!

谢谢!

Diego

迭戈

采纳答案by Sean Patrick Floyd

You are doing nothing wrong, it just can't be done with a HashMap. In a HashMap, order is not guaranteed. Use a TreeMapinstead.

你没有做错任何事,它只是不能用 HashMap 来完成。在 HashMap 中,不能保证顺序。使用 aTreeMap代替。

Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.) This class makes no guarantees as to the order of the map; in particular, it does not guarantee that the order will remain constant over time.

Map接口的基于哈希表的实现。此实现提供了所有可选的映射操作,并允许空值和空键。(HashMap 类大致等同于 Hashtable,只是它是不同步的并且允许空值。)该类不保证映射的顺序;特别是,它不保证订单会随着时间的推移保持不变。

Source:Hashmap

来源:Hashmap

回答by Peter Lawrey

Your check sum cannot depend on the order of entries as HashMap is not ordered. An alternative to using TreeMap is LinkedHashMap (which retains an order), but the real solution is to use a hashCode which doesn't depending on the order of the entries.

您的校验和不能取决于条目的顺序,因为 HashMap 不是有序的。使用 TreeMap 的替代方法是 LinkedHashMap(保留顺序),但真正的解决方案是使用不依赖于条目顺序的 hashCode。

回答by AmitG

Use LinkedHashMap which is order one. TreeMap is not ordered. TreeMap is sorted map. TreeMap sorts elements irrespective of insertion order.

使用 LinkedHashMap,它是一阶。TreeMap 不是有序的。TreeMap 是排序映射。TreeMap 不考虑插入顺序对元素进行排序。