Java Apache Ignite:反序列化后在Hashmap中找不到对象

Java Apache Ignite:反序列化后在Hashmap中找不到对象,java,serialization,hashmap,ignite,binary-serialization,Java,Serialization,Hashmap,Ignite,Binary Serialization,我对ApacheIgnite序列化/反序列化有一个问题,与字段反序列化的顺序有关。我需要在Ignite缓存中放置一个“B”的实例,如下所示: public class A { private final String name; public A(String name) { this.name = name; } public String getName() { return name; } } public c

我对ApacheIgnite序列化/反序列化有一个问题,与字段反序列化的顺序有关。我需要在Ignite缓存中放置一个“B”的实例,如下所示:

public class A {
    private final String name;

    public A(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}


public class B extends A {

    private Map<B, String> mapOfB;

    public B(String name) {
        super(name);

        mapOfB = new HashMap<>();
    }

    public void addB(B newB, String someString) {
        mapOfB.put(newB, someString);
    }

    public Map<B, String> getMap() {
        return mapOfB;
    }

    @Override
    public boolean equals(Object obj) {
        if( obj != null && obj instanceof B) {
            if(this.getName() == null && ((B) obj).getName() == null && this == obj) {
                return true;
            } else if(this.getName().equals(((B) obj).getName())) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int hashCode() {
        return this.getName()==null? System.identityHashCode(this):this.getName().hashCode();
    }
}
公共A类{
私有最终字符串名;
公用A(字符串名称){
this.name=名称;
}
公共字符串getName(){
返回名称;
}
}
公共类B扩展了A{
私人地图;
公共B(字符串名称){
超级(姓名);
mapOfB=新的HashMap();
}
public void addB(B newB,String someString){
mapOfB.put(newB,someString);
}
公共地图getMap(){
返回mapOfB;
}
@凌驾
公共布尔等于(对象obj){
if(obj!=null&&obj实例B){
if(this.getName()==null&&((B)obj).getName()==null&&this==obj){
返回true;
}else if(this.getName().equals((B)obj.getName())){
返回true;
}
}
返回false;
}
@凌驾
公共int hashCode(){
返回this.getName()==null?System.identityHashCode(this):this.getName().hashCode();
}
}
如果运行以下代码,则会出现错误:

    public static void main(String[] args) {
        // write your code here
        B b1 = new B("first");
        b1.addB(b1, "some first string");
        B b2 = new B("second");
        b1.addB(b2, "some second string");

        // init Ignite configuration
        // force java.util.Hashtable to be binary serialized, 
        // it prevents infinite recursion and  other problems 
        // occurring with the Optimized Serializer
        IgniteConfiguration cfg = new IgniteConfiguration();
        BinaryConfiguration binConf = new BinaryConfiguration();
        Collection<String> binClassNames = new LinkedList<>();
        binClassNames.add("java.util.Hashtable");
        binConf.setClassNames(binClassNames);
        cfg.setBinaryConfiguration(binConf);
        Ignition.start(cfg);

        // put b1 in cache
        IgniteCache cache = Ignition.ignite().getOrCreateCache("MyCache");
        cache.put(b1.hashCode(), b1);

        //get b1 from cache
        B b1FromCache= (B) cache.get(b1.hashCode());

        // print map values
        System.out.println("b1 map value: " + b1.getMap().get(b1));
        System.out.println("b1 from cache map value: " + b1FromCache.getMap().get(b1));
    }
publicstaticvoidmain(字符串[]args){
//在这里编写代码
B b1=新的B(“第一”);
b1.addB(b1,“某些首字符串”);
B b2=新的B(“第二”);
b1.addB(b2,“第二个字符串”);
//初始点火配置
//强制对java.util.Hashtable进行二进制序列化,
//它可以防止无限递归和其他问题
//与优化的序列化程序一起发生
IgniteConfiguration cfg=新IgniteConfiguration();
BinaryConfiguration binConf=新的BinaryConfiguration();
集合binClassNames=newLinkedList();
add(“java.util.Hashtable”);
binConf.setClassNames(binClassNames);
setBinaryConfiguration(binConf);
点火起动(cfg);
//将b1放入缓存
IgniteCache cache=Ignition.ignite().getOrCreateCache(“MyCache”);
cache.put(b1.hashCode(),b1);
//从缓存中获取b1
b1FromCache=(B)cache.get(b1.hashCode());
//打印地图值
System.out.println(“b1映射值:+b1.getMap().get(b1));
System.out.println(“b1来自缓存映射值:+b1FromCache.getMap().get(b1));
}
输出为

b1映射值:一些第一个字符串

b1来自缓存映射值:null

问题是子级的字段在父级的字段之前反序列化,因此当Ignite反序列化B时,它首先创建一个空B对象(带有null“name”和“mapOfB”),然后尝试反序列化mapOfB。它创建哈希表,然后反序列化它包含的每个对象以填充它

对于上面示例中的b2,没有问题,因为反序列化b2时还不存在对b2的引用,因此创建一个新的b2对象,填充b2字段(包括“name”字段),然后使用正确的哈希将其添加到Hashmap中

但对于b1,反序列化已启动,因此该对象已存在于Ignit的反序列化对象映射中,但名称为空(b1正在进行反序列化),并且使用此空名称计算哈希代码。哈希表将b1与当时计算的哈希代码放在一起,因此当我们试图在最后的映射中找到具有非空名称的b1时,将找不到它

我无法更改A和B类以及这些对象的创建方式(Hashmap是如何填充的,…),因此必须通过更改序列化来解决。有没有一个简单的方法可以做到这一点


备注:实际代码要比这复杂得多,实际B和Hashmap之间可能有类。

关于这种行为的原因,您是正确的
mapOfB
字段在
name
字段之前反序列化,并且
hashCode()
取决于该名称。而
B
的字段在您将其作为键放入映射后会发生更改,因此它是
hashCode
更改

我建议您更改数据模型,但由于您不能,这里有另一个选项
OptimizedMarshaller
似乎对映射没有问题,因为它使用简单的Java序列化。但是您将无法使用BinaryObject抽象和其他一些特性。 以下是如何启用OptimizedMarshaller:

OptimizedMarshaller封送器=新的OptimizedMarshaller();
塞特马歇尔勒(马歇尔勒);
如果存储的值未实现可序列化接口,则可能需要对其进行适当配置:

marshaller.setRequireSerializable(false);

但是请注意,禁用
requireSerializable
标志可能会以负面的方式影响序列化性能。

可以工作的是类似于
b1.addB(新的B(“第一”),“一些第一个字符串”)
但这是非常脆弱的,并且只会因为
hasCode
equals
是如何实现的
B
不应用作映射的键,因为它不是不可变的。谢谢。我同意这是丑陋的,但我不能改变这个代码。我也同意您的解决方案可以工作,但也不能这样做,因为我甚至无法控制hashMap的填充方式。我将编辑文本以添加此内容,再次感谢。感谢您的回答。您实际上不需要强制使用OptimizedMarshaller;由于Hashmap实现了Serializable,它默认由OptimizedMarshaller使用所有包含的元素进行序列化。但是,这会导致无限递归,因为HashMap包含b1,b1包含HashMap。。。而且OptimizedMarshaller不能阻止无限递归,这就是为什么不能在这里使用它。为了防止这种情况发生,我们必须强制Hashmap(在完整应用程序的许多其他java.util类中)进行二进制序列化