[英]Implementing an equivalent to String.intern() for other objects
我正在嘗試實現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<Long, InternableObject> 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;
}
}
}
我的單元測試(失敗):
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<Object, Object>
:
public class InternMap {
private final Map<Object, Object> identityMap = new HashMap<>();
public static <I extends Internalizable<?>> 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<T> {
public static final InternMap interns = new InternMap();
public default T intern(Class<T> klazz) {
return klazz.cast(internMap.intern(this));
}
class C implements Internalizable<C> { ... }
C x = new C();
x = x.intern(C.class);
由於類型擦除,需要Class<T>
參數。 並發在這里被忽略了。
在Java 8之前,只需使用一個空接口Internalizable作為_marker:interface,並使用靜態InternMap。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.