【问题标题】:Complex HashMap has different hashCode after serialization复杂的HashMap序列化后hashCode不同
【发布时间】:2010-03-19 11:16:20
【问题描述】:

我正在将一个 xml 文件解析成一个复杂的 HashMap,如下所示:

Map<String, Map<String, EcmObject>

EcmObject:

public class EcmObject implements Comparable, Serializable {
    private final EcmObjectType type;
    private final String name;
    private final List<EcmField> fields;
    private final boolean pages;

    // getter, equals, hashCode
}

EcmObjectType:

public enum EcmObjectType implements Serializable {
   FOLDER, REGISTER, DOCUMENT
}

EcmField

public class EcmField implements Comparable, Serializable {
    private final EcmFieldDataType dataType;
    private final EcmFieldControlType controlType;
    private final String name;
    private final String dbname;
    private final String internalname;
    private final Integer length;
    // getter, equals, hashCode
}

EcmFieldDataType

public enum EcmFieldDataType implements Serializable {
    TEXT, DATE, NUMBER, GROUP, DEC;
}

和 EcmFieldControlType

public enum EcmFieldControlType implements Serializable{
    DEFAULT, CHECKBOX, LIST, DBLIST, TEXTAREA, HIERARCHY, TREE, GRID, RADIO, PAGECONTROL, STATIC;
}

我已经通过使用 commons lang 的 EqualsBuilder 和 HashCodeBuilder 覆盖了所有 hashCode 和 equal 方法。现在,当我以这种方式复制 A HashMap 时:

Map<String, Map<String, EcmObject>> m = EcmUtil.convertXmlObjectDefsToEcmEntries(new File("e:\\objdef.xml"));
Map<String, Map<String, EcmObject>> m2;

System.out.println(m.hashCode());

ByteArrayOutputStream baos = new ByteArrayOutputStream(8 * 4096);
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(m);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);

m2 = (Map<String, Map<String, EcmObject>>) ois.readObject();

System.out.println(m.hashCode());
System.out.println(m2.hashCode());

m.hashCode() 不等于与 m2.hashCode()

这是我的输出:

-1639352210
-2071553208
1679930154

另一个奇怪的事情是,例如。 10 次 m 具有相同的哈希码,突然在第 11 次哈希码不同...

有什么想法吗?

【问题讨论】:

  • 如果我理解在调用 oos.writeObject(m) 之后甚至 m.hashCode() 发生变化...
  • 是的,你是对的。这是另一件奇怪的事情

标签: java hashmap hashcode


【解决方案1】:

enum 的哈希码在 JVM 实例中不一致。您可以改用enum.toString() 的哈希码。

【讨论】:

    【解决方案2】:

    由于HashMaphashCode 是根据每个键和值的hashCode 定义的,我会尝试找出哪个 键或元素产生不同的@ 987654324@序列化后。

    【讨论】:

    • 不能是Strings,必须是EcmObject的hashCode
    【解决方案3】:

    好的,正如 Sauer 先生所建议的,我编写了一些测试代码来找出哪个元素具有不同的 hashCode,我发现所有(!)EcmField-Objects 具有不同的 hashCode,但是所有EcmField 的参数有 same 哈希码!!

    这里是 hashcode 和 equals 的实现:

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
    
        EcmField ecmField = (EcmField) o;
    
        return new EqualsBuilder()
                .appendSuper(super.equals(o))
                .append(controlType, ecmField.controlType)
                .append(dataType, ecmField.dataType)
                .append(dbname, ecmField.dbname)
                .append(internalname, ecmField.internalname)
                .append(length, ecmField.length)
                .append(name, ecmField.name)
                .isEquals();
    }
    
    @Override
    public int hashCode() {
        return new HashCodeBuilder(13, 37)
                .append(controlType)
                .append(dataType)
                .append(dbname)
                .append(internalname)
                .append(length)
                .append(name)
                .hashCode();
    }
    

    这是我的测试代码

    EcmField ecmFieldOne = ecmFieldsOne.get(i);
    EcmField ecmFieldTwo = ecmFieldsTwo.get(i);
    
    if (ecmFieldOne.hashCode() != ecmFieldTwo.hashCode()) {
        if (!ecmFieldOne.equals(ecmFieldsTwo)) {
            System.out.println("Field: " + ecmFieldOne.getName() + " != " + ecmFieldTwo.getName());
        }
    
        if (ecmFieldOne.getControlType().hashCode() != ecmFieldTwo.getControlType().hashCode()) {
            System.out.println("ControlType: " + ecmFieldOne.getControlType() + " != " + ecmFieldTwo.getControlType());
        }
        if (ecmFieldOne.getDataType().hashCode() != ecmFieldTwo.getDataType().hashCode()) {
            System.out.println("DataType: " + ecmFieldOne.getDataType() + " != " + ecmFieldTwo.getDataType());
        }
        if (ecmFieldOne.getDbname().hashCode() != ecmFieldTwo.getDbname().hashCode()) {
            System.out.println("Dbname: " + ecmFieldOne.getDbname() + " != " + ecmFieldTwo.getDbname());
        }
        if (ecmFieldOne.getInternalname().hashCode() != ecmFieldTwo.getInternalname().hashCode()) {
            System.out.println("Internalname: " + ecmFieldOne.getInternalname() + " != " + ecmFieldTwo.getInternalname());
        }
        if (ecmFieldOne.getLength().hashCode() != ecmFieldTwo.getLength().hashCode()) {
            System.out.println("Length: " + ecmFieldOne.getLength() + " != " + ecmFieldTwo.getLength());
        }
        if (ecmFieldOne.getName().hashCode() != ecmFieldTwo.getName().hashCode()) {
            System.out.println("Name: " + ecmFieldOne.getName() + " != " + ecmFieldTwo.getName());
        }
    }
    

    并且只输入了前两个 if 子句(if (ecmFieldOne.hashCode() != ecmFieldTwo.hashCode())if (!ecmFieldOne.equals(ecmFieldsTwo))),其他的都是假的

    我不明白...

    【讨论】:

    • 我不知道EqualsBuilder究竟是如何工作的,但是当EcmField直接扩展ObjectObjectequals()方法不是时,你为什么要调用appendSuper()这里真的有用吗?
    • HashCodeBuilder() 每次触发时都会计算出不同的 hashCode...但是在 20 次使用相同数据的测试中仍有约 1 次为孔图提供了不同的 hashcode...
    猜你喜欢
    • 2019-12-02
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多