你不需要排序过的列表。你根本不需要排序。
当对象被添加/删除时,我需要从列表中添加/删除键。
但不是立即进行,删除可以等待。使用一个包含所有活动对象ID以及最多一定比例的已删除对象的ArrayList
。使用单独的HashSet
来跟踪已删除的对象。
private List<ID> mostlyAliveIds = new ArrayList<>()
private Set<ID> deletedIds = new HashSet<>()
我想从整个列表中随机选择几十个元素。
ID selectOne(Random random) {
checkState(deletedIds.size() < mostlyAliveIds.size());
while (true) {
int index = random.nextInt(mostlyAliveIds.size());
ID id = mostlyAliveIds.get(index);
if (!deletedIds.contains(ID)) return ID;
}
}
Set<ID> selectSome(Random random, int count) {
checkArgument(deletedIds.size() <= mostlyAliveIds.size() - count);
Set<ID> result = new HashSet<>();
while (result.size() < count) result.add(selectOne(random));
}
为了维护数据,可以做如下操作
void insert(ID id) {
if (!deletedIds.remove(id)) mostlyAliveIds.add(ID);
}
void delete(ID id) {
if (!deletedIds.add(id)) {
throw new ImpossibleException("Deleting a deleted element);
}
if (deletedIds.size() > 0.1 * mostlyAliveIds.size()) {
mostlyAliveIds.removeAll(deletedIds);
deletedIds.clear();
}
}
唯一棘手的部分是insert
,它必须检查是否已经删除的ID被复活。
delete
确保mostlyAliveIds
中被删除的ID不超过10%。当发生这种情况时,它们会在一次扫描中全部删除(我没有检查JDK源代码,但我希望他们做得对),然后继续进行。
如果死亡ID不超过10%,那么selectOne
的开销平均不超过10%。
我相信它比任何排序都要快,因为摊销复杂度为O(n)
。
contains
测试(其中n是集合大小),但没有简便地访问第i个元素(其中i是任意索引)的方法。 - Christian SemrauList
没有Sort或Sorted属性。它确实有一个Sort方法。但是这个方法的行为与您在此处要求的不同(这与Java的Collections.sort
方法没有区别)。即使在.NET中,保持排序列表也不能提高删除时间。 - Kevin Brock