我正在使用C#,但即使你不知道它,也应该很容易理解这个问题。
这是我的问题:我有一些对象,我希望保留在类似hashset的数据结构中,以便我可以根据int
ID查找它们。这些对象具有可变属性,因此散列它们不是一个选项(我需要一些关于它们的常量哈希,是吗?)。
我所做的是开发以下界面:
public interface IUniqueIDCollection
{
// Can return any int that hasn't been requested yet.
public int RequestUniqueID();
// Undos the requesting of an int
public int ReleaseUniqueID(int uniqueID);
}
我最初的想法是在IUniqueIDCollection
中存储一个内部计数器,该计数器在请求ID时递增。但是,一旦ID被释放,我将不得不跟踪已删除的范围或个人ID。我认为后者会更好。但是如果我使用计数器(或任何循环函数)来生成ID,我就会遇到这样的问题:一旦计数器回绕,我必须检查已经连续请求的ID序列。
启发式是这样的:假设一次最多可以请求5,000个ID。但是,ID经常要求然后发布。释放将倾向于在范围内发生 - 即可能一次请求100,然后所有100将在短时间间隔内释放。
我知道我可以使用GUID或其他东西而不是int,但我想节省ID的空间/带宽/处理时间。
所以我的问题是:考虑到启发式方法,请求和发布方法在上面给出的接口中应该是什么样的伪代码?
答案 0 :(得分:5)
如果您确定已发布的ID可以安全地立即重复使用(即,如果为新对象分配了最近发布的ID,则不会过时引用旧ID,这会让您感到困惑)可以先使用已发布的ID。因此,当ID被释放时,您将其放在队列的末尾。请求新ID时,使用队列中的第一个ID。如果队列为空,则递增内部计数器并给出新数字。
此实施的优点:
缺点:
答案 1 :(得分:1)
在几乎所有情况下,可能比上面的Tom Panning's更糟糕,但您可以使用BitArray来跟踪正在使用的ID。内存使用量与总共拥有实时ID的位数一样多;最坏的情况是512MB用于映射所有32位整数。释放很简单:只需将相应的位设置为0.获取(或请求)ID需要搜索0位,如果找不到,则扩展BitArray。
如果您仍然可以选择扩展BitArray(即您尚未达到512MB),您可能不希望在决定扩展之前搜索所有BitArray - 这样做总是很慢。你肯定不会总是想要从同一个索引开始:跟踪你找到的最后一个0并从那里开始搜索可能是一个好主意。
我可以看到的一个优点是,一旦释放所有或几乎所有对象,就会使用内存。然后Tom Panning的解决方案需要的内存至少是这个内存的32倍。但是,我希望在解决方案使用较少的典型用法中。