为什么指定Map的初始容量会导致后续序列化给出不同的结果?

Why does specifying Map's initial capacity cause subsequent serializations to give different results?

我正在尝试比较 2 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)); // 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
}

由于某些原因,如果MapWrapper包含一个HashMap(或LinkedHashMap并且被初始化为一个初始容量,序列化给出与序列化-反序列化-序列化不同的结果。

我添加了反序列化-序列化的第二次迭代,并与第一次迭代进行了比较。他们总是平等的。差异仅在第一次迭代后出现。

请注意,我必须创建一个 MapWrapper 并将其添加到 State 中的列表中,就像在 main 开始时所做的那样。

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

我正在使用 jdk1.8.0_25 和 Windows7.

为什么会这样?

HashMap source code of readObject 中的以下行和注释解释了差异:

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

确实,查看字节的十六进制,区别在于数字 2(您配置的桶数)和数字 16(默认桶数)。我还没有检查过这个特定字节的含义;但考虑到这是唯一的区别,如果它是其他东西,那将是一个巧合。

<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.
                   ^