依赖于未修改的哈希集的迭代顺序

本文关键字:顺序 迭代 哈希集 依赖于 未修改 | 更新日期: 2023-09-27 18:31:29

HashSet<object> myHashSet = new HashSet<object>();
// Iteration 1
foreach (object myObject in myHashSet) { ... }
// Some instructions THAT DO NOT MODIFY myHashSet
...
// Iteration 2
foreach (object myObject in myHashSet) { ... }

两次迭代中,myHashSet的对象是否会以相同的顺序枚举?

依赖于未修改的哈希集的迭代顺序

根据 HashSet (link) 的参考源,在没有集合修改的情况下,迭代顺序是可预测的。

public bool MoveNext() {
    if (version != set.m_version) {
        throw new InvalidOperationException(SR.GetString(SR.InvalidOperation_EnumFailedVersion));
    }
    while (index < set.m_lastIndex) {
        if (set.m_slots[index].hashCode >= 0) {
            current = set.m_slots[index].value;
            index++;
            return true;
        }
        index++;
    }
    index = set.m_lastIndex + 1;
    current = default(T);
    return false;
}

但是,这不太可能在 .NET 平台的未来版本或其他实现中更改。要确保顺序保持不变,请在第一次迭代时从集合中创建一个列表,并将该列表用于第二次迭代:

var myList = myHashSet.ToList();
foreach( var obj myObject in myList) ...
// Some instructions (may or may not modify myHashSet, it no longer matters)
foreach( var obj myObject in myList) ...

这有两个可能的答案,真的。

第一个是"是的,因为枚举HashSet是确定性的,如果你不相信我,你可以简单地查看源代码"。

第二个是"不,因为如果你想获得技术,HashSet<T>.GetEnumerator()的文档并没有说顺序是确定性的,所以明天实现可能会改变"。

使用第一个答案来完成工作不会错。如果你想绝对地确定,你当然可以使用myHashSet.ToArray()并只迭代它,但仅仅为了技术性而引入副本很可能是矫枉过正的。在这种情况下,实现必须不遗余力地实现确定性的枚举。

不过,一个重要的警告是:强调HashSet在枚举之间不会修改并没有错,因为实现在集合更改时更改顺序合理的(这取决于项在内部的存储方式)。