我的算法需要通过删除元素来迭代缩小集合,并在每次迭代中对删除的元素和缩小集合进行处理。并且:
Python集具有一个pop
成员,几乎可以做到这一点。在Scala and Go中,选择并删除哈希集的“第一个”元素似乎很好(其中“第一个”对应于迭代器)。在Rust中,类似于:
// split off an arbitrary element from a (non-empty) set
pub fn pop<T>(set: &mut HashSet<T>) -> T
where
T: Eq + Clone + std::hash::Hash,
{
let elt = set.iter().next().cloned().unwrap();
set.remove(&elt);
elt
}
与其他语言相比,这似乎是性能瓶颈。我benchmarked some implementations of a pop-like function on the playground,但没有一个表现良好。显然,删除一个元素并不昂贵,但是选择一个元素是:iter().next()
花费了一笔财富(*)。用retain
避免这样做是无济于事的:它总是迭代整个集合。有其他选择吗?
通过仔细检查,PS iter().next()
相当便宜,到目前为止,微基准测试是可以信赖的。 Separate microbenchmarks说从集合中选择任意元素的成本(在我的系统中以纳秒为单位):
| Type of set | Number of elements in set instance
| | 100 | 10,000 | 1,000,000
| Rust HashSet | 2 | 2 | 2
| Rust BTreeSet | 11 | 12 | 13
| Go map[]struct{} | 27 | 31 | 94
| Python set | 125 | 125 | 125
答案 0 :(得分:3)
我正在使用的集合具有整数
请勿使用HashSet
; BTreeSet
具有更好,更一致的性能。
对于N
= 100000 ...
BTreeSet
sequenced : 3065.098µs
pop_1 : 2941.876µs
pop_2 : 2927.429µs
HashSet
sequenced : 3091.454µs
pop_1 : 172547.080µs
pop_2 : 807182.085µs
答案 1 :(得分:2)
我猜想与Can I randomly sample from a HashSet efficiently?中的建议相同:如"sequenced" solution in the benchmark中所示,将集合复制为向量只是为了对其进行迭代:
let seq: Vec<u32> = set.iter().cloned().collect();
for elt in seq {
set.remove(&elt);
这意味着如果您只需要将集合收缩一次(选择任意元素)一次或几次,或者无法廉价地复制集合内容,则此答案不适用。
答案 2 :(得分:0)