为其他对象实现String.intern()的等效项

我正在尝试实现String.intern()的等价物,但对于其他objets。 我的目标如下:我有一个对象A,我将序列化然后反序列化。 如果在某处有另一个对A的引用,我希望反序列化的结果是相同的引用。

这是我期望的一个例子。

MyObject A = new MyObject(); A.data1 = 1; A.data2 = 2; byte[] serialized = serialize(A); A.data1 = 3; MyObject B = deserialize(serialized); // B!=A and B.data1=1, B.data2=2 MyObject C = B.intern(); // Here we should have C == A. Consequently C.data1=3 AND C.data2=2 

这是我的实现atm。 ( MyObject类扩展了InternableObject

 public abstract class InternableObject { private static final AtomicLong maxObjectId = new AtomicLong(); private static final Map dataMap = new ConcurrentHashMap(); private final long objectId; public InternableObject() { this.objectId = maxObjectId.incrementAndGet(); dataMap.put(this.objectId, this); } @Override protected void finalize() throws Throwable { super.finalize(); dataMap.remove(this.objectId); } public final InternableObject intern() { return intern(this); } public static InternableObject intern(InternableObject o) { InternableObject r = dataMap.get(o.objectId); if (r == null) { throw new IllegalStateException(); } else { return r; } } } 

我的unit testing(失败):

  private static class MyData extends InternableObject implements Serializable { public int data; public MyData(int data) { this.data = data; } } @Test public void testIntern() throws Exception { MyData data1 = new MyData(7); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(data1); oos.flush(); baos.flush(); oos.close(); baos.close(); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); MyData data2 = (MyData) ois.readObject(); Assert.assertTrue(data1 == data2.intern()); // Fails here } 

失败的原因是,在反序列化时,会调用InternableObject的构造函数,因此objectId将为2(即使序列化数据包含“1”)

有关如何解决这个特定问题的想法,或另一种处理高级问题的方法?

多谢你们

不要使用构造函数来创建实例。 使用工厂方法检查实例是否已经存在,只创建一个实例(如果还没有匹配的实例)。

要使序列化合作,您的类将需要使用readResolve()/ writeReplace()。 http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serial-arch.html#4539

你实现构造函数的方式,你在构造过程中泄漏了一个引用,这可能导致很难解决问题。 此外,您的实例映射不受任何锁保护,因此它不是线程保存。

通常, intern()形成一个方面 ,并且可能不应该被实现为基类,可能过于限制其在更复杂的星座中的使用。

有两个方面:

1.共享“相同”对象。

当多个对象可以“内化”到同一个对象时,内化对象只会带来利润。 所以我认为,那是InternalableObjecte。 使用新的序号并不是真的足够。 更重要的是,类定义了一个fit equals和hashCode。

然后你可以做一个身份Map

 public class InternMap { private final Map identityMap = new HashMap<>(); public static > Object intern(I x) { Object first = identityMap.get(x); if (first == null) { first = x; identityMap.put(x, x); } return first; } } 

InternMap可用于任何类,但在上面我们将其限制为Internalizable事物。

2.用它的.intern()替换动态创建的非共享对象。

在Java 8中可以使用接口中的defualt方法实现:

 interface Internalizable { public static final InternMap interns = new InternMap(); public default T intern(Class klazz) { return klazz.cast(internMap.intern(this)); } class C implements Internalizable { ... } C x = new C(); x = x.intern(C.class); 

由于类型擦除,需要Class参数。 并发在这里被忽略了。

在Java 8之前,只需使用一个空接口Internalizable作为_marker:interface,并使用静态InternMap。