ObjectIDGenerator
只是在它看到的對象和分配的對象ID上保留一個散列表。
下面是一個簡化的版本在C#:
public class MyObjectIdGenerator
{
private Dictionary<int,List<int>> _hashToID = new Dictionary<int,List<int>>();
private List<object> _objects = new List<object> { null, };
private int _idCounter = 1;
private int _numRemoved = 0;
public int GetId(object obj)
{
if (obj == null)
{
return 0;
}
int hash = RuntimeHelpers.GetHashCode(obj);
List<int> ids;
if (!_hashToID.TryGetValue(hash, out ids))
{
ids = new List<int>();
_hashToID[hash] = ids;
}
foreach (var i in ids)
{
if (ReferenceEquals(_objects[i], obj))
{
return i;
}
}
// Move the counter to the next free slot.
while (_idCounter < _objects.Count && _objects[_idCounter] != null)
{
_idCounter++;
}
int id = _idCounter++;
ids.Add(id);
// Extend the pool to enough slots.
while (_objects.Count <= id) {
_objects.Add(null);
}
_objects[id] = obj;
return id;
}
public bool Remove(object obj)
{
if (obj == null) return false;
// Locate the object
int hash = RuntimeHelpers.GetHashCode(obj);
List<int> ids;
if (!_hashToID.TryGetValue(hash, out ids)) return false;
foreach (var i in ids)
{
if (ReferenceEquals(_objects[i], obj))
{
// Remove the object, and clean up.
_objects[i] = null;
ids.Remove(i);
if (ids.Count == 0)
{
_hashToID.Remove(hash);
}
_numRemoved++;
if (_numRemoved >= 10 && _numRemoved >= _objects.Count/2) {
// Too many free slots. Reset the counter.
_idCounter = 0;
_numRemoved = 0;
}
return true;
}
}
return false;
}
public object GetObject(int id)
{
if (id < 0 || id >= _objects.Count) return null;
// 0 => null
return _objects[id];
}
}
下面是ObjectIDGenerator的反編譯的來源:http://www.fixee.org/paste/30e61ex/
編輯:添加Remove
方法。
順便說一句,如果不再需要在MyObjectIDGenerator中註冊的對象,會發生什麼? MyObjectIDGenerator通過持有引用來保持它們的生命,對吧?這是否阻止未使用的對象被垃圾收集? – Silicomancer
這是正確的。標準的'ObjectIDGenerator'也可以保持對象的活動狀態。如果你需要這些對象是可收集的,你可以將每個對象包裝在一個'WeakReference'中。 –
非常有趣。所以我肯定需要爲我的雙向查找對象ID生成器使用弱引用。如果我正確理解這些弱引用,我需要在使用引用之前檢查「IsAlive」。對?但是,這仍然意味着對象ID生成器會緩存弱引用,即使它是死的。這也意味着對象ID生成器的內部表將無限增長,即使大多數包含的引用已經死亡,對不對? – Silicomancer