为什么指定Map的初始容量会导致后续的序列化产生不同的结果? [英] Why does specifying Map's initial capacity cause subsequent serializations to give different results?

查看:137
本文介绍了为什么指定Map的初始容量会导致后续的序列化产生不同的结果?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图比较2 byte [] 这是同一对象序列化的结果:


  • 1 byte [] 是通过反序列化第一个 byte [] ,然后再次序列化。 数组可以不同。反序列化第一个 byte [] 应该重建原始对象,并且序列化该对象与序列化原始对象相同。所以,2 byte [] 应该是相同的。然而,在某些情况下它们可能是不同的,显然是。

    我正在序列化的对象( State )包含另一个对象( MapWrapper )的列表,该对象又保存一个集合。取决于集合,我从比较代码中得到了不同的结果。



    以下是MCVE:

      import java.io.ByteArrayInputStream; 
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    public class Test {

    public static void main(String [] args){

    State state = new State();
    state.maps.add(new MapWrapper());

    byte [] pBA = stateToByteArray(state);
    State pC = byteArrayToState(pBA);
    byte [] zero = stateToByteArray(pC);
    System.out.println(Arrays.equals(pBA,zero)); //见下面的输出
    状态pC2 = byteArrayToState(pBA);
    byte [] zero2 = stateToByteArray(pC2);
    System.out.println(Arrays.equals(zero2,zero)); //总是为真


    public static byte [] stateToByteArray(State s){

    try {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(s);
    return bos.toByteArray();
    } catch(IOException e){
    e.printStackTrace();
    }
    返回null;


    public static State byteArrayToState(byte [] bytes){

    ObjectInputStream ois;
    尝试{
    ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
    return(State)ois.readObject();
    } catch(IOException | ClassNotFoundException e){
    e.printStackTrace();
    }
    返回null;



    class State实现Serializable {

    private static final long serialVersionUID = 1L;

    列表< MapWrapper> maps = new ArrayList<>();
    }

    类MapWrapper实现了Serializable {

    private static final long serialVersionUID = 1L;

    //不同的选项,选择一个!
    //列表< Integer> ints = new ArrayList<>(); true
    // List< Integer> ints = new ArrayList<>(3); true
    // Map< String,Integer> map = new HashMap<>(); true
    // Map< String,Integer> map = new HashMap<>(2); false

    code $


    由于某些原因,如果 MapWrapper 包含一个 HashMap (或 LinkedHashMap 容量,序列化会产生与序列化 - 反序列化 - 序列化不同的结果。



    我添加了第二次反序列化序列化并与第一次相比。他们总是平等的。注意,我必须创建一个 MapWrapper 并将其添加到列表中。 State 中,就像在 main 开始时所做的那样,导致这种情况。



    据我所知,初始容量只是一个性能参数 。使用默认的或指定的不应该改变行为或功能。



    我使用jdk1.8.0_25和Windows7。



    为什么会发生这种情况?

    解决方案

    以下行和注释在 HashMap readObject 的源代码解释了区别:

      s.readInt(); //读取并忽略桶的数量

    确实,查看字节的十六进制数,数字2(您配置的桶数)和数字16(桶的默认数量)之间。我没有检查过这个字节的含义。但如果是别的,这可能是一个巧合,因为这是唯一的区别。

     < snip> 08 00 00 00 02 00 00 00 78 78 // Original 
    < snip> 08 00 00 00 10 00 00 00 00 78 78 //反序列化+序列化。
    ^


    I am trying to compare 2 byte[] which are the results of serialization of the same object:

    • 1 byte[] is created by serializing the object
    • the other by deserializing the 1st byte[] and then serializing it again.

    I do not understand how these 2 arrays can be different. Deserializing the first byte[] should reconstruct the original object, and serializing that object is the same as serializing the original one. So, the 2 byte[] should be the same. However, under certain circumstances they can be different, apparently.

    The object I am serializing (State) holds a list of another object (MapWrapper) which in turn holds a single collection. Depending on the collection, I get different results from my comparison code.

    Here is the MCVE:

    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class Test {
    
        public static void main(String[] args) {
    
            State state = new State();
            state.maps.add(new MapWrapper());
    
            byte[] pBA = stateToByteArray(state);
            State pC = byteArrayToState(pBA);
            byte[] zero = stateToByteArray(pC);
            System.out.println(Arrays.equals(pBA, zero)); // see output below
            State pC2 = byteArrayToState(pBA);
            byte[] zero2 = stateToByteArray(pC2);
            System.out.println(Arrays.equals(zero2, zero)); // always true
        }
    
        public static byte[] stateToByteArray(State s) {
    
            try {
                ByteArrayOutputStream bos = new ByteArrayOutputStream();
                ObjectOutputStream oos = new ObjectOutputStream(bos);
                oos.writeObject(s);
                return bos.toByteArray();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return null;
        }
    
        public static State byteArrayToState(byte[] bytes) {
    
            ObjectInputStream ois;
            try {
                ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
                return (State) ois.readObject();
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    class State implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        List<MapWrapper> maps = new ArrayList<>();
    }
    
    class MapWrapper implements Serializable {
    
        private static final long serialVersionUID = 1L;
    
        // Different options, choose one!
    //  List<Integer> ints = new ArrayList<>();       true
    //  List<Integer> ints = new ArrayList<>(3);      true
    //  Map<String, Integer> map = new HashMap<>();   true
    //  Map<String, Integer> map = new HashMap<>(2);  false
    }
    

    For some reason, if MapWrapper contains a HashMap (or LinkedHashMap) and is initialized with an initial capacity, the serialization gives a different result than a serialization-deserialization-serialization.

    I added a 2nd iteration of deserialization-serialization and compared to the 1st. They are always equal. The difference manifests only after the first iteration.

    Note that I must create a MapWrapper and add it to the list in State, as done in the start of main, to cause this.

    As much as I know, the initial capacity is a performance parameter only. Using the default one or a specified one should not change behavior or functionality.

    I am using jdk1.8.0_25 and Windows7.

    Why does this happen?

    解决方案

    The following line and comment in the HashMap source code of readObject explains the difference:

    s.readInt();                // Read and ignore number of buckets
    

    Indeed, looking at the hex of the bytes, the difference is between a number 2 (your configured number of buckets) and a number 16 (the default number of buckets). I haven't checked that's what this particular byte means; but it'd be quite a coincidence if it's something else, considering that's the only difference.

    <snip> 08 00 00 00 02 00 00 00 00 78 78   // Original
    <snip> 08 00 00 00 10 00 00 00 00 78 78   // Deserialized+serialized.
                       ^
    

    这篇关于为什么指定Map的初始容量会导致后续的序列化产生不同的结果?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆