带有空和子串的Java序列化

fr1*_*eza 1 java string serialization substring

看看实施情况,但未能想到对此的解释,但也许有人会知道.

public static void main(String[] args) throws Exception {
    List<String> emptyStrings = new ArrayList<String>();
    List<String> emptySubStrings = new ArrayList<String>();
    for (int i = 0; i < 20000; i++) {
        String actuallyEmpty = "";
        String subStringedEmpty = "                                                                 ";
        subStringedEmpty = subStringedEmpty.substring(0, 0);
        emptyStrings.add(actuallyEmpty);
        emptySubStrings.add(subStringedEmpty);
    }
    System.out.println("Substring test");
    // Write to files
    long time = System.currentTimeMillis();
    writeObjectToFile(emptyStrings, "empty.list");
    System.out.println("Time taken to write empty list " + (System.currentTimeMillis() - time));
    time = System.currentTimeMillis();
    writeObjectToFile(emptySubStrings, "substring.list");
    System.out.println("Time taken to write substring list " + (System.currentTimeMillis() - time));
    //Read from files
    time = System.currentTimeMillis();
    List<String> readEmptyString = readObjectFromFile("empty.list");
    System.out.println("Time taken to read empty list " + (System.currentTimeMillis() - time));
    time = System.currentTimeMillis();
    List<String> readEmptySubStrings = readObjectFromFile("substring.list");
    System.out.println("Time taken to read substring list " + (System.currentTimeMillis() - time));
}

private static void writeObjectToFile(Object o, String file) throws Exception {
    FileOutputStream out = new FileOutputStream(file);
    ObjectOutputStream oout = new ObjectOutputStream(out);
    oout.writeObject(o);
    oout.flush();
    oout.close();
}

private static <T> T readObjectFromFile(String file) throws Exception {
    ObjectInputStream ois = null;
    try {
        ois = new ObjectInputStream(new FileInputStream(file));
        return (T) ois.readObject();
    } finally {
        ois.close();
    }
}
Run Code Online (Sandbox Code Playgroud)

最终这两个列表包含20,000个空字符串(一个列表包含""空字符串,另一个包含由子字符串(0,0)生成的空字符串).但是如果检查生成的序列化文件的大小(empty.list和substring.list),您会注意到empty.list包含的实际数据要多得多.

我注意到,取消序列化这些子字符串对象的远程EJB的调用者似乎也有严重的性能问题.

Joh*_*ler 5

列表的大小是不同的,因为java使用一种机制来存储对同一对象的多次引用,如下所述:

对其他对象的引用(瞬态或静态字段除外)也会导致写入这些对象.使用引用共享机制对对单个对象的多个引用进行编码,以便可以将对象图形恢复为与写入原始图像时相同的形状.

请参阅ObjectOutputStream

如果查看生成的序列化文件,您将看到:

内部有1个字符串为空:

empty.list:

ac ed 00 05 73 72 00 13 6a 61 76 61 2e 75 74 69
6c 2e 41 72 72 61 79 4c 69 73 74 78 81 d2 1d 99
c7 61 9d 03 00 01 49 00 04 73 69 7a 65 78 70 00
00 00 01 77 04 00 00 00 01 74 00 00 78
Run Code Online (Sandbox Code Playgroud)

字符串""对应最后三个字节(00 00 78)

substring.list

ac ed 00 05 73 72 00 13 6a 61 76 61 2e 75 74 69
6c 2e 41 72 72 61 79 4c 69 73 74 78 81 d2 1d 99
c7 61 9d 03 00 01 49 00 04 73 69 7a 65 78 70 00
00 00 01 77 04 00 00 00 01 74 00 00 78
Run Code Online (Sandbox Code Playgroud)

请注意,使用一个元素,结果文件是相同的.

但是如果我们想要在同一个对象上添加更多次,我们将面临其他行为.用2倍的字符串查看相应的文件.

empty.list:

ac ed 00 05 73 72 00 13 6a 61 76 61 2e 75 74 69
6c 2e 41 72 72 61 79 4c 69 73 74 78 81 d2 1d 99
c7 61 9d 03 00 01 49 00 04 73 69 7a 65 78 70 00
00 00 02 77 04 00 00 00 02 74 00 00 71 00 7e 00
02 78
Run Code Online (Sandbox Code Playgroud)

substring.list

ac ed 00 05 73 72 00 13 6a 61 76 61 2e 75 74 69
6c 2e 41 72 72 61 79 4c 69 73 74 78 81 d2 1d 99
c7 61 9d 03 00 01 49 00 04 73 69 7a 65 78 70 00
00 00 02 77 04 00 00 00 02 74 00 00 74 00 00 78
Run Code Online (Sandbox Code Playgroud)

请注意,substring继续"normal",两个不相关的字符串具有不同的引用.但是空有一些额外的字节来处理相同引用的问题.

substring(00 00 74 00 00 78)中的六个字节与emptylist(00 00 71 00 7e 00 02 78)中的八个字节

这是错误的,因为您添加的每个重复字符串都会添加更多额外字节.因此,当你填满你的arrayList时,会有很多额外的字节,以便能够以原始的方式重建.

如果你想知道为什么有这种共享机制,我建议你看一下这个问题:

序列化中参考共享的含义是什么?枚举如何序列化?

  • 注意:这似乎只是这种情况,因为序列化时空字符串特别小.对于大多数对象,引用共享将导致共享文件更小,而不是没有共享的文件. (2认同)