簡體   English   中英

為其他對象實現String.intern()的等效項

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM