gpt4 book ai didi

java - Apache 点燃: Object cannot be found in Hashmap after deserialization

转载 作者:行者123 更新时间:2023-12-02 11:57:38 27 4
gpt4 key购买 nike

我在 Apache Ignite 序列化/反序列化方面遇到了与字段反序列化顺序相关的问题。我需要将“B”的实例放入 Ignite 缓存中,如下所示:

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();
}
}

如果我运行以下代码:

    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));
}

输出为

b1 map 值:第一个字符串

b1 来自缓存映射值:null

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

对于上面示例中的 b2 来说,没有问题,因为在反序列化时尚不存在对 b2 的引用,因此会创建一个新的 b2 对象,填充 b2 字段(包括“name”字段),然后添加它到具有正确哈希值的 HashMap 。

然而,对于 b1,反序列化已开始,因此该对象已存在于 Ignit 的反序列化对象映射中,但名称为空(b1 正在进行反序列化),并且具有使用该空名称计算的 hashCode。 Hashtable 将 b1 与当时计算出的 hashCode 放在一起,因此当我们尝试在最后的映射中查找名称非 null 的 b1 时,将无法找到。

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

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

最佳答案

您对于此类行为的原因是正确的。 mapOfB 字段在 name 字段之前反序列化,并且 hashCode() 取决于该名称。当您将 B 作为键放入映射中后,B 的字段会发生变化,因此它是 hashCode 发生变化。

我建议您更改数据模型,但既然您不能,这里还有另一个选择... OptimizedMarshaller 似乎对 map 没有问题,因为它使用一个简单的 Java 序列化。但是您将无法使用 BinaryObject 抽象和一些其他功能。以下是启用 OptimizedMarshaller 的方法:

OptimizedMarshaller marshaller = new OptimizedMarshaller();
cfg.setMarshaller(marshaller);

如果您存储的值没有实现 Serialized 接口(interface),那么您可能需要对其进行适当的配置:

marshaller.setRequireSerializable(false);

但请注意,禁用 requireSerialized 标志可能会对序列化性能产生负面影响。

关于java - Apache 点燃: Object cannot be found in Hashmap after deserialization,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47474188/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com