依赖未修改的HashSet的迭代顺序

时间:2014-11-21 16:16:13

标签: c# mono ienumerable hashset

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的对象是否会以相同的顺序枚举?

2 个答案:

答案 0 :(得分:4)

真的有两种可能的答案。

第一个是“是的,因为列举HashSet是确定性的,如果你不相信我,你可以简单地查看来源。”

第二个是“不,因为如果你想获得技术,HashSet<T>.GetEnumerator()的文档并没有说订单是确定性的,那么明天实施可能会改变”。

完成工作的第一个答案是不错的。如果你想绝对肯定,你当然可以使用myHashSet.ToArray()并且只对它进行迭代,但是为了技术性而引入一个副本可能会有点过分。在这种情况下,实现必须远离以实现确定性的枚举。

一个重要的警告:强调HashSet未在枚举之间进行修改是没有错的,因为 对于实现更改顺序是合理的设置更改(这取决于项目在内部的存储方式)。

答案 1 :(得分:4)

根据HashSetlink)的参考来源,迭代次序在没有设定修改的情况下是可预测的。

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) ...